JSX Email
JSX Email renders React components to email HTML. How it relates to React Email, its Outlook handling, requirements and release status, with read dates.
JSX Email is a library for writing an email as a React component and rendering it to an HTML string. You build the message from its components (Html, Body, Container, Section, Button, Text, Tailwind and others) in a .jsx or .tsx file, then call render().
- Language
- TypeScript, JavaScript (React)
- License
- MIT, free
- Maintainer
- Andrew Powell
- Latest release
- 3.2.1 (2026-05-28)
- Weekly npm downloads
- 97,446
- Source
- shellscape/jsx-email
Figures read on November 5, 2026.
- A JavaScript or TypeScript team that already writes React
- A project on Node.js 22 and React 19.2 or newer
- A CLI check of your template against email client data
- Any sending provider:
renderneeds no account
- A project on Node.js 20 or React 18
- A backend outside JavaScript
- A team that wants a company as maintainer
Minimal example
A template, then the render call, built from the project's Render documentation and the ButtonProps type in its source. Button requires numeric width and height props.
import { Html, Body, Container, Text, Button } from 'jsx-email';export function Welcome({ name }: { name: string }) {return (<Html><Body><Container><Text>Welcome to Acme, {name}</Text><Button href="https://example.com/confirm" width={200} height={44}>Confirm email</Button></Container></Body></Html>);}
import { render } from 'jsx-email';import { Welcome } from './emails/Welcome';const html = await render(<Welcome name="Jane" />);
render returns the HTML string. The package is jsx-email on npm, and the code is on GitHub.
Pros and cons
- CLI
checkagainst caniemail data - Outlook VML fallback on
Button, from props - Works with any provider, no account
- Async components and
<Suspense> - Tailwind through UnoCSS, hashed classes
- Individual maintainer, no corporate backing
- Much smaller adoption than React Email
- Version 3 needs Node.js 22, React 19.2
- No release for months while commits continue
- Open issue:
Headmay blank Classic Outlook
The details behind each point are in Limits and gotchas and Maintenance.
How it works
The points below come from the project's documentation and source, not from our own tests.
- Tables:
Sectionrenders a<table>. The docs sayRow"wraps a table element" but is not designed to mimic a table exactly. - Inlining:
rendertakes aninlineCssoption that converts class names tostyleattributes through@jsx-email/plugin-inline. It is off by default.minifyandprettyare separate options and plugins. - Tailwind: the
Tailwindcomponent is built on UnoCSS with Tailwind and Windi CSS presets. Itsproductionprop replaces class names with hashed ones for Gmail's class limits. - Outlook conditionals: the
Buttonsource emits a VML fallback inside<!--[if mso]>and setsmso-border-alt. AConditionalcomponent writes HTML conditional comments, with anmsoprop or anexpressionprop (not both). - Plain text: the
plainTextoption ofrendertakestrueor an object ofhtml-to-textoptions. - Doctype:
renderreturns a string with an XHTML 1.0 Transitional doctype.
The string is the HTML body alone. Your provider wraps it in a MIME part and adds the headers defined by the message format.
The CLI binary is email, with build, check, create, preview and help. email preview <dir> starts a preview server on http://localhost:55420, and npx create-mail my-email-project scaffolds a project. email check compares a template with caniemail compatibility data and reports unsupported features per email client. You select clients with --email-clients or check.emailClients in the config file.
Relationship to React Email
JSX Email is a separate project with its own maintainer and its own package. GitHub does not mark the repository as a fork, and neither its README nor the pages served on jsx.email use that word. A v2 migration page in the repository source, which now returns a 404 on jsx.email, says the default-export convention was inherited from React Email "when we forked". Its git history starts with React Email commits, including a chore(release): react-email v1.10.0 commit on 2023-09-05, before the first JSX Email commits on the same day. The code descends from React Email, and the authoring model is the same.
The JSX Email docs call React Email "fairly far behind jsx-email in terms of functionality". We did not verify that comparison.
Send it with an email API
render returns a string, so you can pass it to any provider. With the Nuntly TypeScript SDK:
import { Nuntly } from '@nuntly/sdk';import { render } from 'jsx-email';import { Welcome } from './emails/Welcome';const nuntly = new Nuntly({ apiKey: process.env.NUNTLY_API_KEY! });const html = await render(<Welcome name="Jane" />);await nuntly.emails.send({from: 'Acme <hello@example.com>',to: 'jane@example.org',subject: 'Welcome',html,});
The Send email from Next.js guide wires this into a server action.
Email client support
- "Modern email clients"
- Cross-client inconsistencies handled
email checkagainst caniemail data
- Client list
- Client versions tested
- Test method and results
The claim comes from the README, which says the components make templates "that work across modern email clients". We did not run tests ourselves.
Outlook on Windows needs the most care. Button draws a VML shape there from its props, and Head emits a conditional closer that an open issue says blanks Classic Outlook (see Limits and gotchas). Gmail and Outlook on Windows have essentially no dark mode support, as the project's own FAQ says, and caniemail lists no prefers-color-scheme support in either. inlineCss also removes dark mode and media queries. Before you ship, run email check and send a test to Classic Outlook and Gmail.
Limits and gotchas
- Every
Conditionalcloses with<![endif]/-->, and an open issue says this blanks Classic Outlook.Headadds aConditional, so any template using<Head />emits it, andRawrewrites-->the same way (reproduced with 3.2.1). The reporter of issue #403 saw an empty message in Classic Outlook, and the maintainer closed the fix PR unmerged. We did not test Outlook ourselves, so check Classic Outlook, or post-process withhtml.replaceAll('<![endif]/-->', '<![endif]-->'). - Nothing is inlined by default.
Tailwindwrites classes plus a<style>block, so styles depend on the client keeping<style>. caniemail lists Gmail as supporting it only in<head>, with a 16 KB cap, and not in Gmail mobile webmail.inlineCss: trueneeds@jsx-email/plugin-inlineinstalled: without it,renderlogs an error and returns the HTML un-inlined. inlineCssdeletes media queries, dark mode and web fonts. The inline plugin removes every<style>andclassafter copying plain rules, sosm:anddark:classes and@font-faceare gone, and ahover:class maderenderthrow (reproduced with 3.2.1). Choose per template between inlined styles and responsive styles, and avoid pseudo-class utilities when inlining.- Sibling
Conditionalcomponents corrupt the output. Two in the same parent lost the content of one and duplicated the other (reproduced with 3.2.1; no upstream issue found). Wrap eachConditionalin its own element. - Outlook buttons are built from props only.
Buttonhides the link in Outlook and draws a VML shape fromwidth,height,backgroundColor,textColorand the border props, sostyle,classNameand Tailwind classes never reach it. A JSX child such as<strong>printed[object Object]in the VML and a missinghrefprintedhref="undefined"(reproduced with 3.2.1). Pass plain text and anhref, and set the visual props.
Maintenance
Active, with a release gap to watch: the last npm release, 3.2.1, is from 2026-05-28, while commits continue (last one 2026-10-19: CI, repository, preview and website changes).
- Install the single
jsx-emailpackage. The older@jsx-email/*packages are deprecated on npm. The repository has no GitHub Releases, only tags such asjsx-email-v3.2.1, so check npm for the current version. - Commits do not reach npm. A preview feature committed on 2026-06-16 is not in 3.2.1.
- Version 3 needs Node.js 22 and React 19.2 or newer.
Compare
Sibling tools in this catalog:
- React Email: the project whose history JSX Email starts from, maintained by Resend.
- MJML: XML-like markup compiled to HTML, with ports outside Node.
- mjml-react: React components that compile through MJML.
- Maizzle: Vue single-file components with Tailwind CSS.
- Svelte Email: Svelte components.
- @unitpost/email: JSX or TSX, or the same tags as a string with no build step.
The email frameworks hub lists every tool with its status.
FAQ
Is JSX Email a fork of React Email?
GitHub does not mark it as a fork, and neither the README nor the pages served on jsx.email use that word, but a v2 migration page in the repository source says "when we forked". Its git history starts with React Email commits, including a chore(release): react-email v1.10.0 commit on 2023-09-05, so the code descends from React Email. It has its own maintainer, Andrew Powell, and its own package, jsx-email.
Who maintains JSX Email?
Andrew Powell, an individual (GitHub user shellscape). The README says "community-driven project with no corporate sponsorship or backing", and no email sending service runs it. It has generic integration docs for AWS SES, Loops, Nodemailer, Postmark, Resend, Plunk and SendGrid.
Does JSX Email support Tailwind?
Yes, through its Tailwind component, built on UnoCSS with Tailwind and Windi CSS presets. Its production prop swaps class names for hashed ones to stay within Gmail's class limits. The older @jsx-email/tailwind package is deprecated.
Which Node.js and React versions does JSX Email need?
Version 3 requires Node.js 22.0.0 or newer. The README says React 19.1.0 or newer, but the 3.2.1 package declares a peer range of ^19.2.0, so use React 19.2 or later.
Is JSX Email still maintained?
As read on 2026-11-05, it is active, with a gap to watch. The last commit was on 2026-10-19, but the last npm release, 3.2.1, dates from 2026-05-28, which makes it 161 days old. Commits since then are CI, repository, preview and website changes.