mrkeyoor.com_
Wed 23 Sept 00:32 UTC
npmWeb Frontendupdated 22 Sept 2026

react-email review

Our full-package browser build from react-email 6.9.2 reached 1,832.2 KB minified and 564 KB gzipped, which is a warning to keep this toolchain in a server or build process. React Email supplies typed React components for table-based email markup, a local template browser, Tailwind processing, and HTML or plain-text rendering. It does not send mail or prove inbox parity. Current 6.9.3 is a workspace patch release whose preview UI now preserves sidebar state and scroll position while switching templates; the react-email API itself did not change in that release.

Verdict

React Email 6.9.2 took 12.5 seconds and 78 MB to install on our box, with 118 packages, bundled types, and 0 audit findings; its full browser build was 564 KB gzipped. Use current 6.9.3 for server-side React email authoring, and keep delivery plus real-inbox testing outside the preview toolchain.

We installed it

Lab card: what happened when we installed react-emailScreenshot of react-email documentation
Install✓ · 12.5s118 packages on disk · 78 MB
ImportESM import works · require() works · ESM package with exports map
Browser564 KBgzipped (1832.2 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does react-email install cleanly?

Yes. In a fresh container with an empty cache, npm install react-email finished in 13 seconds, leaving 118 packages and 78 MB on disk. npm audit reported no known vulnerabilities.

How much does react-email add to a browser bundle?

564 KB gzipped (1832.2 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does react-email work with both ESM and CommonJS?

Yes. Both import 'react-email' and require('react-email') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does react-email include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

react-email or mjml: which should you use?

mjml: Choose it for an email-specific markup language and compiler without React component semantics. React Email 6.9.2 took 12.5 seconds and 78 MB to install on our box, with 118 packages, bundled types, and 0 audit findings; its full browser build was 564 KB gzipped.

When should you not use react-email?

Your production runtime is below Node 20: current react-email declares node >=20.0.0

API stability3/5The JSX component model and render function are easy to carry across ordinary patch releases, and 6.9.3 changed preview UI behavior without changing the react-email API. Major 6 consolidated components and rendering utilities into react-email and requires Node 20 or newer. That history means an upgrade can affect imports, CLI output, Tailwind processing, and deployment scripts in the same release line.
Docs4/5The official documentation covers components, rendering, CLI commands, preview props, static assets, Tailwind, export, deployment, and integrations with multiple delivery providers. It also states that local static files are not hosted for recipients and that rendering with real props is the primary path. Production teams still need guidance outside this scope for inbox matrices, delivery retries, suppression lists, and provider-side HTML changes.
Maintenance5/5Version 6.9.3 was published on August 25, 2026, the same day the repository was pushed, and GitHub reports 27 open issues and pull requests in an unarchived project. Recent work covers preview navigation state, Tailwind compatibility, encoded static filenames, per-module output for tree shaking, and editor behavior. The package is receiving code, release, documentation, and integration work at a steady pace.
Ecosystem5/5npm recorded 3,799,242 downloads in the week ending August 24, 2026, and the repository has 19,660 stars. Official examples cover Resend, Nodemailer, SendGrid, Mailgun, Postmark, SES, Azure Communication Email, Brevo, Mailtrap, and other transports. React 18 and 19 are accepted peers, while generated HTML and plain text remain provider-neutral. The tradeoff is an authoring stack far larger than a transport-only package.

Use it if

  • Your application already uses React and transactional templates benefit from typed props and shared JSX components
  • Designers and engineers need a local template browser with live updates and per-client compatibility warnings
  • Your server will render HTML and plain text before passing both forms to Resend, SES, Nodemailer, or another transport
  • You maintain enough templates to justify reusable email primitives, Tailwind-to-email processing, and automated export
Skip it if

Setup reality

We installed react-email 6.9.2 in 12.5 seconds on a clean Node 22 image. It left 118 packages and 78 MB on disk. The package declared 22 direct dependencies, 2 peer dependencies, React and React DOM, and npm audit found 0 known vulnerabilities. Its unpacked size was 4,868 KB under MIT, and its engine requires Node 20 or newer.

The CLI reads ./emails unless --dir points elsewhere. It discovers default exports in .js, .jsx, and .tsx files recursively, while folders whose names start with _ stay out of the template list. email dev uses port 3000 by default. Files under emails/static work in local previews only; recipient-facing images need absolute, publicly reachable URLs. PreviewProps supplies sample UI data and does not validate real send data.

Version 6.9.2 is ESM with an exports map, bundled declarations, and separate import and require conditions. Both module styles loaded in our sandbox. Rendering and previewing require no provider credential. Delivery remains a second system with its own SMTP password or API key, retries, suppression handling, and observability. Keep render on the server or in a build job instead of sending the measured 1,832.2 KB browser build to clients.

email export removes its output directory before rebuilding, so dedicate that path to generated files. email build similarly recreates .react-email, installs preview-app dependencies with npm, and must run before email start. Tailwind converts supported classes for email output, but CSS support still differs by inbox. Place Head inside Tailwind when classes generate media rules, inspect plain text separately, and run real inbox tests for the clients your users have.

Patterns

Build a typed transactional template create-typed-template

import { Body, Button, Container, Head, Html, Preview, Text } from 'react-email';

type WelcomeProps = { name: string; verifyUrl: string };

export default function WelcomeEmail({ name, verifyUrl }: WelcomeProps) {
  return (
    <Html lang="en">
      <Head />
      <Preview>Confirm your account</Preview>
      <Body style={{ backgroundColor: '#f6f6f6', fontFamily: 'Arial, sans-serif' }}>
        <Container style={{ backgroundColor: '#fff', padding: '24px' }}>
          <Text>Hello {name},</Text>
          <Button href={verifyUrl}>Verify account</Button>
        </Container>
      </Body>
    </Html>
  );
}

Version 6 exports these components from react-email; validate user data and URLs before rendering.

Give the preview UI sample props define-preview-data

type ReceiptProps = { customer: string; total: string };

function Receipt({ customer, total }: ReceiptProps) {
  return <Text>{customer}, your total is {total}.</Text>;
}

Receipt.PreviewProps = { customer: 'Ada', total: '$42.00' } satisfies ReceiptProps;
export default Receipt;

PreviewProps appears in local tooling and does not become validation or a production default.

Preview a non-default email directory start-template-preview

npx react-email dev --dir ./src/emails --port 3001 --clients outlook,gmail

The clients option filters compatibility checks; it does not run Outlook or Gmail renderers.

Hide shared modules from the sidebar organize-template-files

emails/
  _shared/
    footer.tsx
  account-created.tsx
  password-reset.tsx
  static/
    logo.png

The CLI scans default-exported .js, .jsx, and .tsx templates; a directory whose name starts with _ is treated as support code.

Convert Tailwind classes for email apply-tailwind-styles

import { Body, Head, Html, Tailwind, Text } from 'react-email';

export default function Notice() {
  return (
    <Html>
      <Tailwind>
        <Head />
        <Body className="bg-white p-6">
          <Text className="text-base leading-6 text-slate-900">Payment received</Text>
        </Body>
      </Tailwind>
    </Html>
  );
}

Head belongs inside Tailwind when a class produces a media rule that cannot be inlined.

Point email images at a public host use-hosted-images

import { Img } from 'react-email';

const baseUrl = process.env.PUBLIC_ASSET_URL ?? 'http://localhost:3000';

export function Logo() {
  return <Img src={`${baseUrl}/logo.png`} width="160" height="48" alt="Example" />;
}

The preview server's static directory is local tooling; sent messages need an absolute URL that recipients can fetch.

Render HTML with real application props render-email-html

import { render } from 'react-email';
import WelcomeEmail from './emails/welcome-email';

const html = await render(
  <WelcomeEmail name={user.name} verifyUrl={verifyUrl} />,
);

render is asynchronous in version 6 and belongs in a server or build process.

Generate the plain-text part render-email-text

const text = await render(
  <WelcomeEmail name={user.name} verifyUrl={verifyUrl} />,
  { plainText: true },
);

Inspect the generated links and spacing because plain text is derived from the rendered markup.

Hand both outputs to an SMTP transport send-through-nodemailer

const element = <WelcomeEmail name={user.name} verifyUrl={verifyUrl} />;
const [html, text] = await Promise.all([
  render(element),
  render(element, { plainText: true }),
]);

await transporter.sendMail({
  from: 'Example <hello@example.com>',
  to: user.email,
  subject: 'Confirm your account',
  html,
  text,
});

Nodemailer owns authentication and delivery; react-email only produces the message bodies.

Export readable HTML artifacts export-generated-html

npx react-email export --dir ./emails --outDir ./generated-emails --pretty

The command removes an existing output directory, so never aim it at a folder containing hand-edited files.

Export text files for another backend export-plain-text

npx react-email export --dir ./emails --outDir ./generated-text --plainText

Static export uses template-side sample data; render at send time when each recipient needs different props.

Build the preview app before starting it deploy-preview-build

npx react-email build --dir ./emails
npx react-email start

build recreates .react-email and installs its dependencies; start fails when that generated app is missing.

Alternatives

PackageRegistryPick it when
mjmlnpmChoose it for an email-specific markup language and compiler without React component semantics.
@maizzle/frameworknpmChoose it for HTML templates, Tailwind CSS, layouts, and a static email build pipeline.
email-templatesnpmChoose it when a Node service needs template rendering, previews, localization hooks, and transport integration in one workflow.
nodemailernpmChoose it when HTML and text already exist and SMTP delivery is the remaining job.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.