JSX runtime for MJML email templates. No React required.
npm install @mjmx/core mjml
Pure JSX runtime with its own AST. No react or react-dom needed.
Template literal types for CSS values, colors, percentages. Autocomplete that works.
40+ components with strictly typed attributes. Every MJML tag, fully supported.
Reusable email components with PropsWithChildren, fragments, and conditionals.
render() for HTML output,
serialize() for MJML strings.
Pure string manipulation. No virtual DOM, no reconciler.
import { render } from '@mjmx/core'; const Email = ({ name }: { name: string }) => ( <mjml> <mj-body> <mj-section> <mj-column> <mj-text font-size="20px" color="#333"> Hello {name}! </mj-text> <mj-button href="https://example.com">Click me</mj-button> </mj-column> </mj-section> </mj-body> </mjml> ); const { html, errors } = render(<Email name="World" />);
import { render, type PropsWithChildren } from '@mjmx/core'; const Card = ({ children, title }: PropsWithChildren<{ title: string }>) => ( <mj-section> <mj-column> <mj-text font-weight="bold">{title}</mj-text> {children} </mj-column> </mj-section> ); const Header = () => ( <mj-section background-color="#2c3e50"> <mj-column> <mj-text color="#fff" font-size="24px">My App</mj-text> </mj-column> </mj-section> ); const WelcomeEmail = ({ name }: { name: string }) => ( <mjml> <mj-body> <Header /> <Card title="Welcome!"> <mj-text>Hello {name}, thanks for joining.</mj-text> </Card> </mj-body> </mjml> );
const OrderEmail = ({ items }: Props) => ( <mjml> <mj-body> <mj-section> {items.map(item => ( <mj-column> <mj-text>{item.name}</mj-text> <mj-text>{item.price}</mj-text> </mj-column> ))} </mj-section> </mj-body> </mjml> );
<mjml> <mj-body> <mj-section> {{#each items}} <mj-column> <mj-text>{{this.name}}</mj-text> <mj-text>{{this.price}}</mj-text> </mj-column> {{/each}} </mj-section> </mj-body> </mjml> <!-- No type safety --> <!-- No autocomplete --> <!-- No component reuse -->
Add mjmx and mjml to your project.
$ npm install @mjmx/core mjml
Set the JSX transform in your tsconfig.json.
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@mjmx/core"
}
}
Or use the pragma:
/** @jsxImportSource @mjmx/core */
Create a component and render it to HTML.
import { render } from '@mjmx/core'; const { html } = render( <mjml> <mj-body> <mj-section> <mj-column> <mj-text>Hello World!</mj-text> </mj-column> </mj-section> </mj-body> </mjml> );