Getting started
Introduction
MJML is a markup language designed to reduce the pain of coding responsive emails. Its semantic syntax makes it easy and straightforward.
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>Hello World</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
Installation
# NPM
npm install mjml
# Yarn
yarn add mjml
# Global CLI
npm install -g mjml
Other options:
- Online editor: https://mjml.io/try-it-live
- Desktop app: https://mjmlio.github.io/mjml-app
- VS Code plugin: Search "MJML" in extensions
Basic Structure
<mjml>
<mj-head>
<!-- Metadata, styles, fonts -->
</mj-head>
<mj-body>
<!-- Email content -->
</mj-body>
</mjml>
Every MJML document needs <mjml> root, optional <mj-head>, and <mj-body>.
Layout Components
mj-section
Horizontal container for columns. Creates table rows in output.
<mj-section background-color="#f0f0f0" padding="20px">
<!-- columns here -->
</mj-section>
| Attribute | Description | Default |
|---|---|---|
background-color |
Section background | none |
padding |
Padding around section | 20px 0 |
full-width |
Full viewport width | false |
text-align |
Text alignment | center |
mj-column
Vertical division within section. Must be inside <mj-section>.
<mj-section>
<mj-column width="50%">
<mj-text>Left column</mj-text>
</mj-column>
<mj-column width="50%">
<mj-text>Right column</mj-text>
</mj-column>
</mj-section>
| Attribute | Description | Default |
|---|---|---|
width |
Column width | auto |
padding |
Column padding | none |
background-color |
Column background | none |
vertical-align |
Vertical alignment | top |
Note: Columns stack on mobile by default.
mj-group
Groups columns to prevent stacking on mobile.
<mj-section>
<mj-group>
<mj-column width="50%">
<mj-text>Always</mj-text>
</mj-column>
<mj-column width="50%">
<mj-text>Side by side</mj-text>
</mj-column>
</mj-group>
</mj-section>
mj-wrapper
Wraps multiple sections with common styling.
<mj-wrapper background-color="#eee" padding="20px">
<mj-section>
<mj-column>...</mj-column>
</mj-section>
<mj-section>
<mj-column>...</mj-column>
</mj-section>
</mj-wrapper>
Content Components
mj-text
Display text content with formatting.
<mj-text font-size="16px" color="#333" line-height="1.5" align="left">
Your text content here
</mj-text>
| Attribute | Description | Default |
|---|---|---|
color |
Text color | #000000 |
font-family |
Font family | Ubuntu, Helvetica |
font-size |
Font size | 13px |
line-height |
Line height | 1 |
align |
Text alignment | left |
padding |
Padding | 10px 25px |
mj-image
Display images with responsive sizing.
<mj-image
src="image.jpg"
alt="Description"
width="200px"
href="https://example.com"
/>
| Attribute | Description | Default |
|---|---|---|
src |
Image source | none (required) |
alt |
Alt text | none |
width |
Image width | parent width |
height |
Image height | auto |
href |
Link URL | none |
align |
Alignment | center |
mj-button
Call-to-action button with link.
<mj-button
href="https://example.com"
background-color="#007bff"
color="#ffffff"
border-radius="4px"
padding="10px 25px"
>
Click Here
</mj-button>
| Attribute | Description | Default |
|---|---|---|
href |
Button link | none |
background-color |
Button background | #414141 |
color |
Text color | #ffffff |
border-radius |
Corner radius | 3px |
font-size |
Font size | 13px |
inner-padding |
Inner padding | 10px 25px |
mj-divider
Horizontal line separator.
<mj-divider border-color="#ccc" border-width="1px" width="100%" />
mj-spacer
Vertical spacing between elements.
<mj-spacer height="20px" />
mj-table
Display HTML tables (use for data tables).
<mj-table>
<tr>
<th>Header 1</th>
<th>Header 2</th>
</tr>
<tr>
<td>Data 1</td>
<td>Data 2</td>
</tr>
</mj-table>
mj-social
Social media icons with links.
<mj-social mode="horizontal" icon-size="32px">
<mj-social-element name="facebook" href="https://facebook.com/page" />
<mj-social-element name="twitter" href="https://twitter.com/user" />
<mj-social-element name="instagram" href="https://instagram.com/user" />
</mj-social>
Supported networks: facebook, twitter, instagram, linkedin, youtube, github, and more.
mj-navbar
Navigation menu with links.
<mj-navbar base-url="https://example.com">
<mj-navbar-link href="/about"> About </mj-navbar-link>
<mj-navbar-link href="/contact"> Contact </mj-navbar-link>
</mj-navbar>
mj-raw
Inject raw HTML (use sparingly).
<mj-raw> <div class="custom-html"> Raw HTML here </div> </mj-raw>
Head Components
mj-attributes
Define default styles for components.
<mj-head>
<mj-attributes>
<mj-all font-family="Arial, sans-serif" />
<mj-text padding="0" color="#333" />
<mj-button background-color="#007bff" border-radius="4px" />
</mj-attributes>
</mj-head>
Use <mj-all> for all components or target specific components.
mj-breakpoint
Set responsive breakpoint width.
<mj-head>
<mj-breakpoint width="480px" />
</mj-head>
Default: 480px
mj-font
Load custom web fonts.
<mj-head>
<mj-font
name="Roboto"
href="https://fonts.googleapis.com/css?family=Roboto"
/>
</mj-head>
mj-preview
Preview text shown in inbox (before email is opened).
<mj-head>
<mj-preview> This text appears in the inbox preview </mj-preview>
</mj-head>
mj-style
Add custom CSS styles.
<mj-head>
<mj-style>
.custom-class {
color: red;
}
</mj-style>
<mj-style inline="inline">
.inline-style {
font-weight: bold;
}
</mj-style>
</mj-head>
mj-title
Set email title (HTML <title> tag).
<mj-head>
<mj-title>My Email Subject</mj-title>
</mj-head>
mj-include
Include external MJML files.
<mj-head>
<mj-include path="./header.mjml" />
</mj-head>
Advanced Components
mj-accordion
Collapsible content sections.
<mj-accordion>
<mj-accordion-element>
<mj-accordion-title> Section 1 </mj-accordion-title>
<mj-accordion-text> Content for section 1 </mj-accordion-text>
</mj-accordion-element>
<mj-accordion-element>
<mj-accordion-title> Section 2 </mj-accordion-title>
<mj-accordion-text> Content for section 2 </mj-accordion-text>
</mj-accordion-element>
</mj-accordion>
Note: Requires CSS3 support (limited email client support).
mj-carousel
Image carousel/slider.
<mj-carousel>
<mj-carousel-image src="image1.jpg" href="https://example.com" />
<mj-carousel-image src="image2.jpg" href="https://example.com" />
<mj-carousel-image src="image3.jpg" href="https://example.com" />
</mj-carousel>
Note: Limited email client support (works in Apple Mail, Outlook.com).
mj-hero
Hero image with centered overlay content.
<mj-hero
mode="fluid-height"
background-url="hero-bg.jpg"
background-color="#2a3448"
padding="100px 0"
>
<mj-text color="#ffffff" font-size="32px" font-weight="bold" align="center">
Hero Title
</mj-text>
<mj-button href="https://example.com" background-color="#fff" color="#2a3448">
Call to Action
</mj-button>
</mj-hero>
Common Attributes
Padding
<!-- All sides -->
<mj-text padding="20px">Text</mj-text>
<!-- Vertical | Horizontal -->
<mj-text padding="20px 10px">Text</mj-text>
<!-- Top | Right | Bottom | Left -->
<mj-text padding="10px 20px 30px 40px"> Text </mj-text>
<!-- Individual sides -->
<mj-text
padding-top="10px"
padding-right="20px"
padding-bottom="10px"
padding-left="20px"
>
Text
</mj-text>
Colors
<!-- Named colors -->
<mj-section background-color="red">
<!-- Hex colors -->
<mj-section background-color="#ff0000">
<!-- RGB/RGBA -->
<mj-section background-color="rgb(255, 0, 0)">
<mj-section
background-color="rgba(255, 0, 0, 0.5)"
></mj-section></mj-section></mj-section
></mj-section>
Typography
<mj-text
font-family="Arial, sans-serif"
font-size="16px"
font-weight="bold"
font-style="italic"
line-height="1.5"
letter-spacing="1px"
text-decoration="underline"
text-transform="uppercase"
>
Styled text
</mj-text>
Borders
<!-- All sides -->
<mj-text border="1px solid #ccc"> Text </mj-text>
<!-- Individual sides -->
<mj-text
border-top="1px solid #ccc"
border-right="2px dashed red"
border-bottom="1px solid #ccc"
border-left="2px dashed red"
>
Text
</mj-text>
<!-- Border radius -->
<mj-button border-radius="8px"> Rounded button </mj-button>
Sizing
<!-- Width (px, %, auto) -->
<mj-column width="300px">
<mj-column width="50%">
<mj-image width="200px">
<!-- Height -->
<mj-spacer height="30px" />
<mj-image height="100px" /></mj-image></mj-column
></mj-column>
Alignment
<!-- Horizontal alignment -->
<mj-text align="left">Left</mj-text>
<mj-text align="center">Center</mj-text>
<mj-text align="right">Right</mj-text>
<!-- Vertical alignment (columns) -->
<mj-column vertical-align="top">
<mj-column vertical-align="middle">
<mj-column vertical-align="bottom"></mj-column></mj-column
></mj-column>
CLI Usage
Compile MJML to HTML
# Basic compilation
mjml input.mjml -o output.html
# Output to stdout
mjml input.mjml
# Multiple files
mjml *.mjml -o output-folder/
Watch Mode
# Watch single file
mjml -w input.mjml -o output.html
# Watch directory
mjml -w templates/*.mjml -o output/
Configuration Options
# Minify output
mjml --config.minify true input.mjml
# Beautify output
mjml --config.beautify true input.mjml
# Custom validation level
mjml --config.validationLevel soft input.mjml
# Keep comments
mjml --config.keepComments true input.mjml
Validation levels:
strict: Stop on any errorsoft: Log warnings, continueskip: No validation
Configuration File
Create .mjmlconfig in project root:
{
"beautify": true,
"minify": false,
"validationLevel": "soft",
"keepComments": false
}
Node.js API
Basic Usage
import mjml2html from "mjml";
const htmlOutput = mjml2html(`
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>Hello World</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
`);
console.log(htmlOutput.html);
With Options
const htmlOutput = mjml2html(mjmlString, {
beautify: true,
minify: false,
validationLevel: "soft",
keepComments: false,
fonts: {
"Open Sans": "https://fonts.googleapis.com/css?family=Open+Sans",
},
});
// Check for errors
if (htmlOutput.errors.length > 0) {
console.error(htmlOutput.errors);
}
// Get HTML
console.log(htmlOutput.html);
Read from File
import fs from "fs";
import mjml2html from "mjml";
const mjmlContent = fs.readFileSync("./template.mjml", "utf8");
const htmlOutput = mjml2html(mjmlContent);
fs.writeFileSync("./output.html", htmlOutput.html);
Custom Components
import { registerComponent } from "mjml-core";
import MyCustomComponent from "./MyCustomComponent";
registerComponent(MyCustomComponent);
const html = mjml2html(mjmlWithCustomComponent);
Responsive Patterns
Default Behavior
MJML is mobile-first. Columns stack automatically on mobile.
<!-- Desktop: 2 columns | Mobile: stacked -->
<mj-section>
<mj-column width="50%">
<mj-text>Column 1</mj-text>
</mj-column>
<mj-column width="50%">
<mj-text>Column 2</mj-text>
</mj-column>
</mj-section>
Prevent Stacking with mj-group
<!-- Always side-by-side -->
<mj-section>
<mj-group>
<mj-column width="50%">
<mj-text>Always</mj-text>
</mj-column>
<mj-column width="50%">
<mj-text>Together</mj-text>
</mj-column>
</mj-group>
</mj-section>
Custom Breakpoint
<mj-head>
<mj-breakpoint width="600px" />
</mj-head>
Columns stack below this width.
Mobile-Specific Styles
<mj-head>
<mj-style>
@media only screen and (max-width: 480px) {
.mobile-hide {
display: none !important;
}
.mobile-full-width {
width: 100% !important;
}
}
</mj-style>
</mj-head>
<mj-body>
<mj-section>
<mj-column>
<mj-text css-class="mobile-hide"> Hidden on mobile </mj-text>
</mj-column>
</mj-section>
</mj-body>
Examples
Hello World Email
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text font-size="20px" color="#626262" align="center">
Hello World!
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
2-Column Layout
<mjml>
<mj-body>
<mj-section background-color="#f4f4f4">
<mj-column width="50%">
<mj-image src="product1.jpg" alt="Product 1" />
<mj-text align="center"> Product 1 </mj-text>
<mj-button href="https://example.com/1"> View Details </mj-button>
</mj-column>
<mj-column width="50%">
<mj-image src="product2.jpg" alt="Product 2" />
<mj-text align="center"> Product 2 </mj-text>
<mj-button href="https://example.com/2"> View Details </mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
Button with CTA
<mjml>
<mj-body>
<mj-section background-color="#ffffff">
<mj-column>
<mj-text font-size="24px" font-weight="bold" align="center">
Special Offer!
</mj-text>
<mj-text align="center"> Get 50% off your first purchase </mj-text>
<mj-button
href="https://example.com/offer"
background-color="#007bff"
color="#ffffff"
border-radius="8px"
font-size="16px"
font-weight="bold"
inner-padding="15px 40px"
>
Claim Offer
</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
Responsive Product Card
<mjml>
<mj-head>
<mj-attributes>
<mj-all font-family="Arial, sans-serif" />
<mj-text padding="10px" />
</mj-attributes>
</mj-head>
<mj-body background-color="#f4f4f4">
<mj-section background-color="#ffffff" border-radius="8px" padding="20px">
<mj-column>
<mj-image src="product.jpg" alt="Product" border-radius="8px" />
<mj-text font-size="20px" font-weight="bold" align="center">
Premium Product
</mj-text>
<mj-text color="#666" align="center">
High quality product description that explains the benefits.
</mj-text>
<mj-text
font-size="24px"
font-weight="bold"
color="#007bff"
align="center"
>
$99.99
</mj-text>
<mj-button
href="https://example.com/buy"
background-color="#007bff"
border-radius="25px"
inner-padding="12px 30px"
>
Add to Cart
</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
Email with Header and Footer
<mjml>
<mj-head>
<mj-attributes>
<mj-all font-family="Helvetica, Arial" />
</mj-attributes>
</mj-head>
<mj-body background-color="#f0f0f0">
<!-- Header -->
<mj-section background-color="#333" padding="20px">
<mj-column>
<mj-text
color="#ffffff"
font-size="24px"
font-weight="bold"
align="center"
>
Company Name
</mj-text>
</mj-column>
</mj-section>
<!-- Content -->
<mj-section background-color="#ffffff" padding="40px 20px">
<mj-column>
<mj-text font-size="18px"> Hello {{ name }}, </mj-text>
<mj-text> Your email content goes here. </mj-text>
</mj-column>
</mj-section>
<!-- Footer -->
<mj-section background-color="#333" padding="20px">
<mj-column>
<mj-social mode="horizontal" icon-size="32px">
<mj-social-element name="facebook" href="https://facebook.com" />
<mj-social-element name="twitter" href="https://twitter.com" />
</mj-social>
<mj-text color="#aaa" font-size="12px" align="center">
© 2026 Company Name
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
Gotchas
Default Width
MJML emails default to 600px width (desktop standard). Change with:
<mj-body width="800px">
<!-- Content -->
</mj-body>
Button Clickability
mj-button is not fully clickable in Outlook desktop (only text is clickable, not padding area). This is an Outlook limitation.
Workaround: Increase inner-padding to make text area larger.
<mj-button inner-padding="15px 50px"> Wide Button </mj-button>
Column Stacking
Columns always stack on mobile unless wrapped in <mj-group>. Default breakpoint is 480px.
<!-- These stack on mobile -->
<mj-section>
<mj-column width="50%">A</mj-column>
<mj-column width="50%">B</mj-column>
</mj-section>
<!-- These stay side-by-side -->
<mj-section>
<mj-group>
<mj-column width="50%">A</mj-column>
<mj-column width="50%">B</mj-column>
</mj-group>
</mj-section>
Background Images
Background images have limited support in email clients. Use background-url attribute:
<mj-section
background-url="bg.jpg"
background-size="cover"
background-color="#555"
>
<!-- Fallback color shown if image fails -->
</mj-section>
Note: Outlook desktop versions don't support background images well. Always provide a background-color fallback.
Custom Fonts
Web fonts have limited email client support. Always provide fallbacks:
<mj-head>
<mj-font
name="Roboto"
href="https://fonts.googleapis.com/css?family=Roboto"
/>
<mj-attributes>
<mj-all font-family="Roboto, Arial, sans-serif" />
</mj-attributes>
</mj-head>
CSS Support
Email clients have limited CSS support. Avoid:
- Flexbox, Grid
position: absolute/fixed- JavaScript
- External stylesheets (use inline styles)
MJML handles inline styles automatically.
Column Widths
Column widths must sum to 100% (or use auto). Invalid:
<!-- BAD: 300px + 400px = 700px > 600px default -->
<mj-section>
<mj-column width="300px">A</mj-column>
<mj-column width="400px">B</mj-column>
</mj-section>
Fix: Use percentages or ensure pixel widths fit:
<!-- GOOD -->
<mj-section>
<mj-column width="50%">A</mj-column>
<mj-column width="50%">B</mj-column>
</mj-section>
Padding in Columns
Columns have no default padding. Text content uses default padding from mj-text. Control spacing:
<mj-column padding="20px">
<!-- Adds padding around column -->
<mj-text padding="0">
<!-- Remove text padding -->
Content
</mj-text>
</mj-column>
Also see
- MJML Documentation - Official docs
- MJML Try it Live - Online editor
- MJML GitHub - Source code
- MJML Desktop App - Desktop editor
- Email Client CSS Support - CSS compatibility guide