React Email and Tailwind CSS give TypeScript teams a component-based way to author transactional email. React describes the message structure, Tailwind supplies utility styling, and the render step produces HTML that your provider can send.
The important boundary is the same as any email workflow: React and Tailwind run during development or server-side rendering. The recipient receives HTML and plain text not React, JavaScript, or a Tailwind runtime.
Install React Email
Follow the current React Email setup for your package manager, then keep templates in a dedicated directory such as emails/.
A useful structure is:
emails/
components/
EmailButton.tsx
EmailFooter.tsx
EmailLayout.tsx
fixtures/
welcome.ts
WelcomeEmail.tsx
Keep email components separate from web UI. They can share types, formatting functions, and design tokens, but email markup has different compatibility constraints.
Build a Tailwind-styled component
import {
Body,
Button,
Container,
Head,
Heading,
Html,
Preview,
Section,
Tailwind,
Text,
} from 'react-email';
type WelcomeEmailProps = {
firstName: string;
dashboardUrl: string;
};
export function WelcomeEmail({
firstName,
dashboardUrl,
}: WelcomeEmailProps) {
return (
<Html lang="en">
<Head />
<Preview>Your workspace is ready</Preview>
<Tailwind>
<Body className="m-0 bg-slate-100 font-sans text-slate-900">
<Container className="mx-auto max-w-[600px] px-5 py-10">
<Section className="rounded-xl bg-white px-8 py-10">
<Text className="m-0 text-sm font-semibold text-blue-700">
Welcome aboard
</Text>
<Heading className="mt-3 mb-0 text-3xl font-bold leading-tight">
Hi {firstName}, your workspace is ready
</Heading>
<Text className="mt-5 mb-0 text-base leading-7 text-slate-600">
Create your first project and invite your team.
</Text>
<Button
href={dashboardUrl}
className="mt-7 rounded-lg bg-blue-600 px-6 py-3 font-semibold text-white no-underline"
>
Open dashboard
</Button>
</Section>
</Container>
</Body>
</Tailwind>
</Html>
);
}
Use email primitives for document structure and ordinary complete Tailwind classes for styling. Avoid dynamically assembling utilities because Tailwind must see full class tokens.
Why use the Tailwind wrapper?
The React Email Tailwind component processes utility classes for the email render. It provides a familiar styling model while keeping the template within React Email's primitives.
That does not make every web utility safe. Complex Grid, positioning, transforms, and JavaScript interactions still conflict with email-client limitations. Prefer colors, typography, spacing, widths, borders, and simple layout patterns.
Read what actually works with Tailwind in email.
Define typed template props
Treat props as the public contract of the message:
type InvoiceEmailProps = {
customerName: string;
invoiceNumber: string;
totalFormatted: string;
dueDateFormatted: string;
invoiceUrl: string;
};
Pass display-ready values instead of database models. This keeps currency, date, and localization decisions in application services where they can be tested independently.
Provide fixture props for preview and automated tests. Include long names, missing optional sections, translated content, and large amounts.
Create email-specific components
Extract a component when it owns compatibility or a repeated design decision not merely because JSX is longer than a web component.
Strong candidates include:
- document layout and preheader;
- constrained container;
- bulletproof button;
- logo header;
- receipt table;
- divider;
- support block; and
- compliance footer.
Keep the component API small. A button usually needs href, children, and a limited visual variant. Passing arbitrary class strings everywhere weakens the design system.
Render HTML and plain text
Use React Email's render utility in a pure function that returns subject, HTML, and text:
import { render } from '@react-email/render';
export async function renderWelcomeEmail(props: WelcomeEmailProps) {
const component = <WelcomeEmail {...props} />;
return {
subject: `Welcome, ${props.firstName}`,
html: await render(component),
text: await render(component, { plainText: true }),
};
}
Await rendering before calling the provider. Validate that both outputs are non-empty and that required links remain present.
Send through any provider
Keep transport outside the component:
const message = await renderWelcomeEmail(data);
await transport.send({
to: user.email,
from: '[email protected]',
subject: message.subject,
html: message.html,
text: message.text,
});
This separation makes provider migrations, previews, tests, and queued delivery much easier.
Responsive React Email
Start with a single-column mobile-friendly default. Breakpoint utilities need retained media queries and client testing.
Avoid making a critical action depend on hidden, sm:block, or a complex reordering rule. Responsive styles should adjust spacing, type, and optional column behavior.
Use the responsive Tailwind email guide for safe patterns.
Dark mode
Dark-mode utilities can generate prefers-color-scheme rules, but clients may apply their own automatic transformations or ignore your overrides.
Define explicit light-mode colors, add dark mode as an enhancement, and verify logos, borders, muted text, and CTA contrast. Never assume the browser preview predicts Gmail or Outlook.
Test templates at four levels
- Type and data tests: required props, formatting, and URLs.
- Structural tests: heading hierarchy, preheader, alt text, links, and prohibited elements.
- Rendered fixtures: focused snapshots for shared components.
- Inbox tests: Gmail, Outlook, Apple Mail, and mobile.
Snapshot the rendered artifact, not React's internal tree. Keep snapshots small enough for reviewers to understand meaningful changes.
Common problems
Tailwind classes are missing
Use complete static class names. Confirm the template is inside the source paths scanned by the Tailwind integration.
The email looks right in preview but wrong in Outlook
Preview is browser rendering. Simplify the layout, use email primitives, and inspect the rendered HTML for table fallbacks and unsupported CSS.
The render function returns a promise
Await it. Do not pass an unresolved value or an empty placeholder to the provider.
Web components do not work
Web components may depend on scripts and browser CSS. Rebuild them with React Email primitives while sharing only safe tokens and formatting logic.
Frequently asked questions
Does React Email support Tailwind CSS?
Yes. React Email provides a Tailwind wrapper for utility-class styling. Email-client support still depends on the generated CSS and markup.
Can I use my Tailwind configuration?
Use the configuration mechanisms supported by your installed React Email version and keep values email-friendly. Test custom colors, spacing, and fonts in rendered output.
Does React Email inline all CSS?
Inspect the rendered output. Base styles and conditional CSS have different needs; responsive and dark-mode rules cannot simply become unconditional inline declarations.
Can React Email templates be used outside Resend?
Yes. Render HTML and plain text, then send them through any provider or SMTP transport.
React Email or TailwindMail?
Choose React Email when components belong in a React/TypeScript codebase. Choose TailwindMail when you want Tailwind source plus a visual editor and email-safe export. Compare all three approaches in React Email vs. MJML vs. TailwindMail.
Keep components modern and output conservative
React Email and Tailwind improve source quality: typed props, reusable components, familiar utilities, and fast previews.
Use those tools to generate straightforward email HTML, then test the rendered artifact. Modern authoring and conservative output are complementary, not contradictory.
Share with friends