Next.js 13 Server Actions发邮件时客户端组件调用错误求助
完整错误
尝试从服务端调用/email/contact-form-email.tsx的默认导出,但它是客户端组件。无法从服务端调用客户端函数,仅能将其作为组件渲染或传递给客户端组件的props。
环境说明
使用Next.js 13的Server Actions发送邮件,依赖包版本:
"resend": "^0.16.0" "@react-email/components": "^0.0.7"
用于邮件服务和邮件样式设置。
现有代码
sendEmail.ts(Server Action文件)
'use server' import React from 'react'; import { getErrorMessage, validateData } from '@/lib/utils'; import { Resend } from 'resend'; import ContactFormEmail from '@/email/contact-form-email'; const resend = new Resend(process.env.RESEND_API_KEY); export const sendEmail = async(formData : FormData) => { const senderEmail = formData.get('senderEmail'); const contactMessage = formData.get('contactMessage'); try { console.log('sender email : ' + senderEmail); console.log('contact message : ' + contactMessage); resend.emails.send({ from: 'Contact form <onboarding@resend.dev>', to: 'test@gmail.com', subject: 'Message from contact form of portfolio.', reply_to: senderEmail as string, react : React.createElement(ContactFormEmail, { contactMessage: contactMessage as string, senderEmail : senderEmail as string, }) }); } catch (error : unknown) { return { error: getErrorMessage(error) } } }
contact-form-email.tsx(邮件模板文件)
'use client' import React from 'react' import { Html, Body, Head, Heading, Hr, Container, Preview, Section, Text } from '@react-email/components' import { Tailwind } from '@react-email/tailwind' type ContactFormEmailProps = { contactMessage : string, senderEmail : string, } function ContactFormEmail({contactMessage, senderEmail } : ContactFormEmailProps) { return ( <Html> <Head/> <Preview>New message from you portfolio site</Preview> <Tailwind> <Body> <Container> <Section> <Heading>You received the following message from the contact form</Heading> <Text>{contactMessage}</Text> <Hr/> <Text>The sender's email is : {senderEmail}</Text> </Section> </Container> </Body> </Tailwind> </Html> ); } export default ContactFormEmail
其他错误场景
- 若不给
contact-form-email.tsx设置'use client',会触发:
错误由在'./email/contact-form-email.tsx'中导入'@react-email/tailwind/dist/index.mjs'导致。
可能其中一个文件需要标记为客户端入口"use client":
./email/contact-form-email.tsx ./actions/sendEmail.ts
- 若不给
sendEmail.ts设置'use server',会触发:
Error: Missing API key. Pass it to the constructor
new Resend("re_123")
解决方法
核心矛盾是旧版@react-email/tailwind不支持服务端渲染,导致邮件模板必须标记为客户端组件,但Server Action需要在服务端调用它。以下两种方案可解决:
方案1:替换Tailwind为原生内联样式
移除@react-email/tailwind依赖,改用组件内置样式属性,让邮件模板无需标记'use client',直接在服务端渲染:
修改后的contact-form-email.tsx:
import React from 'react' import { Html, Body, Head, Heading, Hr, Container, Preview, Section, Text } from '@react-email/components' type ContactFormEmailProps = { contactMessage : string, senderEmail : string, } function ContactFormEmail({contactMessage, senderEmail } : ContactFormEmailProps) { return ( <Html> <Head/> <Preview>New message from your portfolio site</Preview> <Body style={{ fontFamily: 'Arial, sans-serif', backgroundColor: '#f4f4f4', padding: '20px' }}> <Container style={{ maxWidth: '600px', margin: '0 auto', backgroundColor: '#fff', padding: '30px', borderRadius: '8px' }}> <Section> <Heading style={{ fontSize: '24px', color: '#333', marginBottom: '20px' }}>You received the following message from the contact form</Heading> <Text style={{ fontSize: '16px', color: '#555', lineHeight: '1.6', marginBottom: '20px' }}>{contactMessage}</Text> <Hr style={{ border: 'none', borderTop: '1px solid #eee', margin: '20px 0' }}/> <Text style={{ fontSize: '14px', color: '#666' }}>The sender's email is : {senderEmail}</Text> </Section> </Container> </Body> </Html> ); } export default ContactFormEmail
方案2:升级@react-email/tailwind到兼容服务端的版本
新版@react-email/tailwind已修复服务端渲染问题,升级后可移除邮件模板的'use client'指令:
- 升级依赖:
npm install @react-email/tailwind@latest
- 修改
contact-form-email.tsx,去掉'use client':
import React from 'react' import { Html, Body, Head, Heading, Hr, Container, Preview, Section, Text } from '@react-email/components' import { Tailwind } from '@react-email/tailwind' type ContactFormEmailProps = { contactMessage : string, senderEmail : string, } function ContactFormEmail({contactMessage, senderEmail } : ContactFormEmailProps) { return ( <Html> <Head/> <Preview>New message from your portfolio site</Preview> <Tailwind> <Body className="font-sans bg-gray-100 p-5"> <Container className="max-w-xl mx-auto bg-white p-6 rounded-lg"> <Section> <Heading className="text-2xl text-gray-800 mb-4">You received the following message from the contact form</Heading> <Text className="text-base text-gray-600 leading-relaxed mb-4">{contactMessage}</Text> <Hr className="border-gray-200 my-4"/> <Text className="text-sm text-gray-500">The sender's email is : {senderEmail}</Text> </Section> </Container> </Body> </Tailwind> </Html> ); } export default ContactFormEmail
两种方案均可让Server Action正常调用邮件模板,同时保留样式效果,解决服务端/客户端组件的冲突问题。
内容的提问来源于stack exchange,提问作者0_0perplexed

