mjml-react
mjml-react turns React components into MJML, which compiles to email HTML. Maintenance-only. Example, limits, sending the HTML and the Wix package.
mjml-react (npm package @faire/mjml-react) lets you write an MJML email as React components. You compose MjmlSection, MjmlColumn and MjmlButton in JSX or TSX, the library turns the tree into an MJML string, and the mjml package turns that string into email HTML. The library produces no HTML itself.
- Language
- TypeScript (React)
- License
- MIT, free
- Maintainer
- Faire
- Latest release
- 4.0.1 (2026-06-01), maintenance-only
- Weekly npm downloads
- 144,496
- Source
- Faire/mjml-react
Figures read on November 5, 2026.
- A React team that wants MJML's layout components
- A Node.js backend that can keep
mjmlas a peer dependency - Any sending provider:
renderneeds no account - A move off the abandoned Wix
mjml-reactpackage
- A backend outside JavaScript
- A setup that needs plain text, a preview server or a CLI built in
- A project that wants fewer packages in the render path
Minimal example
The package exports an async render(email, options) at @faire/mjml-react/utils/render. It converts the React tree to MJML, compiles it with mjml, and resolves to { html, errors }.
import { Mjml, MjmlBody, MjmlSection, MjmlColumn, MjmlButton } from '@faire/mjml-react';import { render } from '@faire/mjml-react/utils/render';const { html, errors } = await render(<Mjml><MjmlBody width={500}><MjmlSection><MjmlColumn><MjmlButton padding="20px" backgroundColor="#346DB7" href="https://example.com/">I like it!</MjmlButton></MjmlColumn></MjmlSection></MjmlBody></Mjml>,{ validationLevel: 'soft' },);
The template is shortened from the project's README example. The README wires the same steps by hand: mjml2html(renderToMjml(email)). Install @faire/mjml-react, mjml, react and react-dom: mjml and React are peer dependencies.
Pros and cons
- Components generated from MJML definitions
- Typed props, though units go unchecked
- Peer range covers MJML 5 and React 19
- Renders with
mjmlalone, no account - Backwards compatible with the Wix package on v2, per the README
- Two packages in the render path
- No plain text, preview server or CLI
- Invalid MJML throws by default
- Numeric props silently dropped
- Few commits, maintenance-only
The details behind each point are in Limits and gotchas and Maintenance.
How it works
render calls renderToMjml(element), which walks your React tree and writes an MJML string, then hands that string to mjml2html. Since v3 the components come from a script that reads the MJML component definitions, which the project intends to track every component of the latest MJML version. Every component prop is typed in TypeScript.
After that step MJML does the work: the table layout and the Outlook markup come from mjml2html, not from this library. MJML inlines only the CSS of <MjmlStyle inline>, and all other CSS stays in a <style> block in the <head>. This library's repository does not document the output, so read the MJML page for it. For what your HTML becomes on the wire, see MIME and the message format.
The README lists the MJML options the library sets by default in render: keepComments: false, beautify: false, minify: false, a minifyOptions block (collapseWhitespace, minifyCss, removeComments: "safe", removeEmptyAttributes) and validationLevel: 'strict'. You override them through the second argument of render. If you call mjml2html(renderToMjml(email)) yourself, as the README's hand-written helper does, those defaults do not apply and MJML's own defaults do.
Send it with an email API
The render step gives you an HTML string. Pass it to the Nuntly TypeScript SDK.
import { Nuntly } from '@nuntly/sdk';import { render } from '@faire/mjml-react/utils/render';const nuntly = new Nuntly({ apiKey: process.env.NUNTLY_API_KEY! });const { html } = await render(<Welcome />);await nuntly.emails.send({from: 'Acme <hello@example.com>',to: 'jane@example.org',subject: 'Welcome to Acme',html,});
Welcome stands for your own template component. The Send your first email guide covers the client setup and the response.
Email client support
- Email rendering issues kept to a minimum
- Output that works on mobile
- Client list
- Client versions tested
- Dark mode
- Own VML code for Outlook
The claim comes from the README, which names no email client. Client support is MJML's, so the MJML page covers what MJML says. We did not run the tests.
The only Outlook code in this library is MjmlConditionalComment, which writes an <!--[if gte mso 9]> block and takes a string. The tables Outlook on Windows needs come from MJML. There is no dark mode support: a prefers-color-scheme rule you write in <MjmlStyle> reaches the <style> block unchanged, but caniemail lists it as unsupported in Gmail, Outlook on Windows and Yahoo! Mail. MjmlFont loads fonts through <link> and @import, which caniemail lists as unsupported in Gmail. Before you ship, send a test to Outlook on Windows and Gmail, and look up any CSS property you depend on in caniemail.
Limits and gotchas
renderthrows on invalid MJML instead of fillingerrors. The default validation level is'strict', soawait render(...)rejects with aValidationErrorand the returnederrorsarray stays empty. Wrap the call intry/catch, or pass{ validationLevel: 'soft' }and readerrors, for example when attributes hold template placeholders such as{{brand}}, which fail strict validation.- Numeric props are silently wrong or dropped. A number becomes
pxonly for a fixed list of props such aswidth,paddingandlineHeight. About 19 other props that TypeScript accepts as numbers, such asmaxHeight,gap,iconSizeandborder, lose the attribute with no warning, andlineHeight={1.5}becomes1.5px. Pass strings with units, for example"50%"or"100px"(issue #87, open since 2023). MjmlConditionalCommentonly takes a string. A JSX child renders<!--[if gte mso 9]>[object Object]<![endif]-->, which breaks the Outlook-only markup you meant to add. Write the Outlook markup as a string. ThefixConditionalCommentutil shown in the README was removed in v3.- v4 changes two things at once.
renderbecame async and themjmlpeer moved from 4 to 5, so an upgrade from 3.x needsawaitat every call site and a newmjmlmajor in the same step. Code and tutorials written for v3 fail or compile against the wrong MJML. - Native Node ESM needs the
.jssuffix on subpaths. The package has noexportsmap, so in an ESM project@faire/mjml-react/utils/renderfails withERR_MODULE_NOT_FOUND. Import@faire/mjml-react/utils/render.js(andextensions/index.js) instead. Bundlers resolve the short form, which the examples above use.
Maintenance
Maintenance-only: the last release, v4.0.1, is from 2026-06-01, and releases before it were months apart (nothing between 2025-09-02 and 2026-04-30).
- Install
@faire/mjml-react, notmjml-react. The Wix package is abandoned (last release 2022-05-20) but still draws about 40,000 weekly downloads, so old tutorials lead to it. Faire's v2 line is backwards compatible with it, so switch first and upgrade later. - Expect an async
renderfrom v4. It moved to MJML 5 and maderenderasynchronous. - Expect slow review. One maintainer account answers, and three outside PRs opened between 2026-05-12 and 2026-08-31 have no reply.
Compare
mjml-react is one way to write MJML. These siblings cover the other ways to author email.
- MJML: the markup language and compiler that mjml-react calls, if you would rather write MJML directly.
- React Email: React components that render to email HTML without MJML.
- React Email vs MJML: components against markup, with the same template in both.
- JSX Email: React components too, with its own maintainer.
- 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
What is the difference between mjml-react and @faire/mjml-react?
mjml-react is the original package from Wix, abandoned since 2022. @faire/mjml-react is the fork Faire maintains. Faire's v2 line is described as backwards compatible with the Wix package, so moving is an install change first.
Is mjml-react still maintained?
As read on 2026-11-05, @faire/mjml-react is maintenance-only. The latest release is v4.0.1 from 2026-06-01, which followed the MJML 5 upgrade in v4.0.0. Commits are infrequent, and the 2026 ones are mostly dependency and CI work.
Does mjml-react produce the HTML itself?
No. It converts your React tree into an MJML string with renderToMjml. render then passes that string to mjml2html from the mjml package, which returns html and errors. The table layout and the Outlook markup come from MJML, which inlines only the CSS of <MjmlStyle inline>.
Does mjml-react work with React 19?
The peer dependency range in @faire/mjml-react 4.0.1 covers React 16 to 19 and mjml ^5.0.0, as read on npm on 2026-11-05.
Can I use mjml-react in the browser?
The README offers mjml-browser as an alternative to mjml, but its Limitations section says the libraries are meant to be run inside a node. The README does not reconcile the two statements, so treat server-side rendering as the supported path.