React Email
Updated
Send React Email templates with Lumail
React Email handles the template layer: components, email-safe markup and a local preview server. Lumail handles sending: your verified domain, delivery tracking and the subscriber record. The bridge between them is one HTML string.
TL;DR
lumail, @react-email/components and @react-email/render. Write the template as a component, call await render(<Template {...props} />) to get HTML and await render(..., { plainText: true }) for the text part, then pass both to lumail.emails.send({ from, to, subject, html, text }). Lumail does not accept a react field, so always render first.Prerequisites
- A Node.js 20+ project that can compile TSX on the server (Next.js, TanStack Start, Remix, or a plain TypeScript setup).
- A Lumail API token that starts with
lum_. - A sending domain verified in the same Lumail organization as the token.
1. Install the packages
2. Set your environment variables
Rendering happens on your server, next to the send call, so the token lives in the same server environment. Never render and send from the browser.
3. Write the template
Keep templates in an emails/ folder. Props make them reusable: the same component renders every customer's receipt.
Use the React Email primitives instead of raw divs. They output the table-based markup and inline styles email clients still need.
4. Preview it locally
The react-email dev dependency ships a preview server that hot-reloads every template in the folder. PreviewProps on the component fills it with sample data.
5. Render to HTML and send
render is async and returns a string. Render twice: once for HTML and once with plainText: true for the text part, which keeps the plain-text version faithful to your copy.
This file contains JSX, so give it a .tsx extension. Run it only on the server.
Handle errors
Rendering errors are thrown by React like any other render error, before Lumail is called. API errors come back from the SDK as { data: null, error }, never thrown, so check error explicitly.
If Lumail rejects the body with validation_error, make sure you pass html and not a react element. Lumail rejects the react, template and attachments fields.
| Status | error.name | What to do |
|---|---|---|
| 401 | missing_api_key | Missing or invalid token. Check the env var on the server that sends. |
| 403 | missing_permission | The token lacks the emails permission. Mint one that has it. |
| 402 | plan limit | The organization used its plan's email volume. Upgrade or wait for the next period. |
| 4xx | validation_error | Missing field, more than one body format, or a from domain that is not verified. |
| 429 | RECIPIENT_RATE_LIMITED | That mailbox hit 5 sends in 10 minutes or 20 in 24 hours. Honor Retry-After. |
| none | network_error / timeout | The request never got an answer. Safe to retry with the same idempotency key. |
Make retries safe with idempotency
Rendering is deterministic for the same props, so retrying with the same idempotency key is safe: Lumail returns the email it already queued and ignores the second body.
Verify your sending domain
Lumail only sends from a domain you have verified. Add the domain in your organization's Domains settings, then publish the SPF, DKIM and DMARC records it shows at your DNS provider. Until the domain verifies, every send fails with an error saying the domain is not authorized or verified.
Use a subdomain such as mail.yourdomain.com if your root domain already sends from another provider. Start DMARC at p=none, then tighten it once reports look clean.
- Email domains - Add a domain and the SPF, DKIM and DMARC records.
- Add a DMARC record - Publish a policy, then tighten it safely.
- Mail tester - Send a real email and check authentication and spam signals.
Production checklist
- Each template sets a
<Preview>line, which inbox lists show next to the subject. - Templates are tested in the preview server at mobile width and in dark mode before shipping.
- The
fromaddress is on a domain verified in the same organization as the token, with SPF, DKIM and a DMARC record. LUMAIL_API_KEYis set on every environment that sends, never committed, never in a public or client-side variable. Use one token per environment.- Every send that can be retried (webhooks, queues, jobs) passes an idempotency key derived from the triggering event.
- One-time codes and magic links set
tracking: { links: false, open: false }so links are not rewritten. - End-to-end tests send to fixture addresses such as
[email protected], which return an id without sending real email. - A failed email is logged with
error.nameand never fails the user's request on its own.
Frequently asked questions
Can I pass a React component straight to Lumail?
No. Lumail accepts html, markdown or tiptap, and rejects a react field. Render the component to an HTML string with render from @react-email/render and send that string.
Is render from @react-email/render synchronous?
No. Current versions return a Promise, so await it. Pass { plainText: true } as the second argument to get the plain-text version of the same template.
Do I need to send a plain-text version?
It is good practice. If you omit text, Lumail generates it from your HTML. Rendering it yourself with plainText: true gives you control over how buttons and links read in text-only clients.
Can I use React Email templates for Lumail campaigns?
Campaigns use Lumail's own editor and Markdown, which keep unsubscribe links, segments and campaign analytics correct. Use React Email for transactional email sent through the API: receipts, password resets, invites and notifications.
Does this work with Next.js and TanStack Start?
Yes. Render and send inside a route handler, server action, server function or server route. The framework guides show where the send call belongs in each one.
Keep building
- GuideSend emails from Stripe webhooks with LumailVerified webhooks that send receipts and failed-payment emails once.
- GuideSend emails from Next.js with LumailApp Router route handlers and server actions with the lumail SDK.
- GuideSend emails from Node.js with LumailA script, an Express route, batch sends and a safe retry helper.
- DocsReact Email tutorialThe docs version of the React Email setup.
- DocsSDK: lumail.emails.sendSignature, options and the result shape.
- DocsSend Email API referenceEvery field, option and error for POST /api/v2/emails.
- Free toolMail testerCheck SPF, DKIM, DMARC and spam signals on a real send.
- DocsLumail docsAPI reference, SDK and tutorials.
Send your first email from code today.
3,000 emails a month free. Transactional and marketing email on one verified domain, with unlimited subscribers on every plan.