Developer Tools

React Email vs MJML

React Email or MJML for HTML email? The same welcome template in both, a dated table, who maintains each, Outlook handling and when to pick which.

Olivier Bazoud
November 5, 2026
11 min read

React Email and MJML both turn code into table-based HTML email that you send through any provider. The difference is the authoring model. React Email has you write React components, MJML has you write XML-style markup.

Pick React Email if
  • Your app is JavaScript or TypeScript with React
  • You want templates as typed components next to your code
  • A Tailwind component and a plain-text helper matter to you
Pick MJML if
  • You want one template language, independent of your app framework
  • Your sending service is not written in JavaScript
  • You want the engine to generate the Outlook wrappers

Both render locally and need no account. Each tool has its own page: React Email and MJML.

At a glance

React EmailMJML
AuthoringReact components in JSX or TSXXML-style tags such as mj-section and mj-text
Reference runtimeNode.js, React required (react-email 6.11.6 declares engines.node >=20.19.0)Node.js (MJML 5 dropped Node 16 and 18, the 6.0.0 alpha needs Node 22 or later)
Other languagesNone in the project's docs or READMEPorts for Rust, Go, Python, Ruby and Elixir, each with its own maintainer
MaintainerResend, an email sending serviceMailjet, an email sending service
LicenseMITMIT
Latest releasereact-email 6.11.8, 2026-11-06v5.4.1, 2026-09-10, plus a v6.0.0 alpha on 2026-10-09
GitHub stars19,91218,284
Weekly npm downloads7,921,788 for react-email2,686,448 for mjml
CSSInline style props, or the built-in Tailwind component (tailwindcss 4.1.12)Components write inline styles, juice runs only for mj-style with inline="inline"
OutlookButton emits <!--[if mso]> blocks. No VML in any component source we searched.mj-section wraps its tables in <!--[if mso | IE]> and uses VML for background images
Plain texttoPlainText(html)None documented
Local previewemail dev, a watching preview appCLI watch mode (mjml -w), no dev server documented
Client supportREADME claim, no published test matrixA per-component matrix on the project website, which we did not read
StatusActiveActive

Figures read on 2026-11-05, except the latest react-email release, read on 2026-11-09.

Pros and cons

React Email

Pros
  • Components, render and preview CLI in one package
  • Plain-text version with toPlainText
  • Built-in Tailwind component, for a subset of Tailwind
  • Frequent releases, the most downloaded of the two
Cons
  • JavaScript and TypeScript only, React required
  • Tailwind component has documented limits
  • @react-email/components is deprecated
  • dark: classes ignored in Gmail

MJML

Pros
  • Reference engine, with ports in other languages
  • Outlook wrappers generated by the engine, VML for backgrounds
  • Active development, v6 in alpha
Cons
  • Rendering needs Node.js (22+ for v6)
  • Three major lines in about ten months
  • mj-include off by default since v5
  • No plain-text output
  • No dark mode support in v5

The same welcome template in both

The same message, written once per tool: a greeting with the recipient's name, one line of text and a button. Both are unstyled, so the output shows the structure each tool adds and nothing else.

React Email 6, a component with a typed name prop:

TypeScript
import { Html, Body, Container, Text, Button } from 'react-email';
export function Welcome({ name }: { name: string }) {
return (
<Html lang="en">
<Body>
<Container>
<Text>Welcome to Acme, {name}</Text>
<Text>Confirm your address to finish setting up your account.</Text>
<Button href="https://example.com/confirm">Confirm email</Button>
</Container>
</Body>
</Html>
);
}
TypeScript
import { render } from 'react-email';
const html = await render(<Welcome name="Jane" />);

MJML 5, a markup string. MJML has no props, so a JavaScript template literal fills the name here:

JavaScript
import mjml2html from 'mjml';
const welcome = (name) => `
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>Welcome to Acme, ${name}</mj-text>
<mj-text>Confirm your address to finish setting up your account.</mj-text>
<mj-button href="https://example.com/confirm">Confirm email</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
`;
const { html, errors } = await mjml2html(welcome('Jane'));

Four differences show up in the two blocks.

  • Layout: React Email nests Container inside Body. MJML makes the grid explicit: every mj-column sits in an mj-section, and the engine turns that into nested tables.
  • Return value: render resolves to the HTML string. mjml2html resolves to an object { html, json, errors }, so you read the markup from html. It is async in current MJML, so code written for v4 needs an await.
  • Data: React Email takes data as typed props. MJML takes none, so you fill values with your own templating, for example with a template literal as above.
  • Tooling: a .tsx file gets your editor's type checking, an .mjml file gets the engine's validation levels and the vscode-mjml plugin listed in the README.

Deeper differences

Rendering and Outlook

Both outputs are table-based with inline styles. React Email components emit inline style attributes, and Section is built on a <table>. MJML describes its output as "responsive email HTML with nested tables and inline CSS". Its components write inline styles themselves, and juice runs only for an mj-style tag with inline="inline".

Outlook is where the two differ in what we could read. The MJML output skeleton declares the VML and Office XML namespaces, and mj-section wraps its tables in <!--[if mso | IE]> blocks and uses VML for background images.

In React Email, the Button component emits <!--[if mso]> blocks and sets mso-padding-alt and mso-text-raise. A search of every React Email component source finds mso only in Button and Font, and no VML. Row is a plain table and Column a plain <td>.

Both projects publish client support as a claim. React Email's README says:

All components were tested using the most popular email clients

Its table ticks Gmail, Apple Mail, Outlook, Yahoo! Mail, HEY and Superhuman with no versions or test method.

MJML says it is "responsive by design on most-popular email clients, even Outlook" and publishes a per-component matrix on its website, with documented gaps such as the mj-navbar hamburger menu working only in Apple Mail. We tested neither. Send yourself a message from your template and open it in the clients your audience uses.

Plain text and CSS

React Email returns a text version with toPlainText(html), and render accepts a plainText option. MJML documents no plain-text output, so you write the text part yourself.

For styling, React Email takes style props or the built-in Tailwind component, which maps className utilities to styles and has four documented limits (no React context across it, no space-* utilities, no prose class, no complex selectors). MJML's mj-style takes an inline="inline" attribute, which is the only case where juice inlines your CSS.

Stack and ecosystem

React Email needs a JavaScript runtime and React, with no port to another language in its docs or README. MJML renders in Node. The project repository lists ports for Rust (MRML), Elixir, Ruby, Python, .NET and PHP, headed "not directly supported by the MJML team". Go renderers (gomjml, mgml, mjml-go) are separate projects that the list does not include.

Ports can trail the Node engine: the Go ones target MJML 4, and so does the stable Felix Schwarz Python release. A backend in Go or Python can render MJML without a Node step, but it will not always match the Node output. The MJML page lists each port with its status.

Package churn

React Email 6.0.0 moved the components and render utilities into the single react-email package. @react-email/components is flagged deprecated on npm and still had 11,888,844 weekly downloads on 2026-11-05, so tutorials that import it are common. MJML 5 changed several defaults: mj-include is ignored unless you opt in, mj-body generates the <body> tag, and mjml2html is async. Read the upgrade notes before you move a v4 project.

Gotchas to know before you pick

The blocking points for each tool. Each tool page has the full list.

React Email

  • The Tailwind component supports only a subset of Tailwind. React context does not cross it, prose does not work, space-* utilities do not work, complex selectors are not supported, and pixelBasedPreset leaves breakpoints and radius in rem, which Outlook on Windows does not support.
  • Responsive, hover and dark classes depend on a <style> block. Outlook on Windows ignores @media and :hover, Gmail drops <style> for non-Google accounts in its apps, and dark: classes do nothing in Gmail, Outlook on Windows or Yahoo! Mail. The preview's dark toggle does not show them.
  • Code from before mid-2024 calls render without await. render became async in @react-email/render 1.0.0, and renderAsync was removed in 2.0.0.

MJML

  • Upgrading v4 code breaks quietly. mjml2html has been async since v5, and mj-include is ignored in the Node API without a warning unless you pass ignoreIncludes: false.
  • Columns are mobile-first. Desktop widths come only from a media query in the <head>, so a client or sending tool that strips head CSS shows the stacked layout on desktop.
  • Outlook for Windows gets VML only for backgrounds. mj-button has none, so rounded corners disappear in Outlook, and the button is fully clickable only with a width in px.

Maintenance and ownership

Both projects are MIT licensed, and each is maintained by a company that runs an email sending service. Neither needs an account to render.

  • React Email is maintained by Resend, under the GitHub organization resend. The repository field on npm pointed to zenorocha/react-email in 1.3.0 (2022-12-21), to resendlabs/react-email in 1.6.1 (2023-01-12) and to resend/react-email in current versions. The default branch is canary, the last commit is from 2026-11-05, and react-email has had five majors from 2.0.0 (2024-01-19) to 6.0.0 (2026-04-16).
  • MJML is maintained by Mailjet. The README header reads "Developed by" with the Mailjet logo, and the license file reads "Copyright (c) 2016 Mailjet SAS". Commits on master in October 2026 come from several GitHub accounts, and the last one is from 2026-10-23. The repository also lists Mailjet's hosted MJML editor, which can send through Mailjet, and the engine does not need it.

Both are active. React Email shipped five releases between 2026-10-14 and 2026-10-29. MJML has nine stable 5.x releases from 5.0.0 (2026-04-16) to 5.4.1 (2026-09-10).

If neither fits, the email frameworks hub lists the other tools, such as mjml-react and Maizzle.

FAQ

Is React Email better than MJML?

Neither is better in general, because they use different authoring models. React Email fits a React codebase and returns a plain-text version with toPlainText. MJML fits teams that want one markup language and a backend that is not JavaScript. As read on 2026-11-05, both are active, MIT licensed and need no account at the service that maintains them.

Does React Email use MJML?

No. React Email renders its own components to HTML, with Section built on a table and a Button that emits MSO conditional comment blocks. MJML is a separate engine. mjml-react is a different project that compiles React components through MJML.

Can I use MJML in a React project?

Yes. MJML is a Node package, so you can call mjml2html from a React or Next.js server. You write the template as MJML markup, not as components, and mjml2html returns a promise. Keep the call on the server and send the html string through your provider.

Which is easier to use with a non-Node backend?

MJML, through a port. MRML renders MJML in Rust, gomjml and mgml in Go, and there are two Python ports and an Elixir package. Check the MJML version each port targets, because some sit on MJML 4. React Email has no port to another language, so a non-Node service needs a Node step.

Which one supports Tailwind?

React Email, through its built-in Tailwind component (tailwindcss 4.1.12 on 2026-11-05). It has four documented limits: no React context across it, no space-* utilities, no prose class and no complex selectors. MJML documents no Tailwind support. Its mj-style tag takes an inline="inline" attribute for plain CSS.

Do I need a Resend or Mailjet account?

No. render returns an HTML string and mjml2html returns an object with an html string, both locally. You can send the result through any provider.

Ready to get started?

Send the HTML your template renders

Free plan available. No credit card required.

Start for free