NexusCS

MJML

HTML
Quick reference for MJML (Mailjet Markup Language) - responsive email markup language that makes coding responsive emails easy.
email
responsive
markup

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:

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 error
  • soft: Log warnings, continue
  • skip: 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