Skip to main content

Our email CSS engine is open source. Give it a star on GitHub 

Emailens
Blog

MJML Guide: Tags, Columns, Outlook Code, Full Template

MJML compiles tags like mj-section and mj-column into nested tables with inline styles and Outlook conditional comments; columns stack below a 480px breakpoint unless you wrap them in mj-group.

The checker needs a computer screen.

A free account opens all 21 clients and keeps your checks for 7 days. Create a free account →

Philippe Kam, founder of Emailens · Published 4 Mar 2026 · Updated 28 Oct 2026

@emailens/engine 0.18.22 · data last changed 10 Nov 2026

Part of the HTML email guide · all guides

MJML is a markup language that compiles to responsive HTML email. You write semantic tags, and MJML writes the nested tables, inline CSS, media queries and Outlook conditional comments. This guide covers installing it, every tag you need for a real template, the head settings most people miss, and how to check the compiled HTML.

What MJML does

When you write <mj-section> and <mj-column>, MJML compiles them to nested <table> elements with precise width calculations, media queries for responsiveness, and inlined styles for maximum CSS compatibility. For Outlook on Windows it adds MSO conditional tables, the same ghost tables you would otherwise write by hand.

The result is HTML that works in Outlook, Gmail, Apple Mail, and the other major clients, without you writing the underlying table markup.

Install MJML and compile your first file

Terminal
10 lines
npm install mjml

# Compile an MJML file to HTML
npx mjml input.mjml -o output.html

# Watch mode for development
npx mjml --watch input.mjml -o output.html

# Validate without compiling
npx mjml --validate input.mjml

From Node, call mjml2html(). In MJML 5 it is async and returns a Promise, so if you are upgrading from v4, update your build scripts to await the result.

MJML tags you will use in every template

mj-body

Root container. Sets background color and width for the entire email.

mj-section

Horizontal row. Compiles to a <table> with one <tr>. Accepts background-color, padding, and full-width.

mj-column

Vertical column within a section. Automatically calculates percentage widths based on sibling count.

mj-group

Wraps columns that must stay side by side on mobile instead of stacking.

mj-wrapper

Wraps several sections so they share one background or border.

mj-text

Text block with full inline CSS support. Wraps content in a <td> with proper padding.

mj-image

Responsive image with automatic width calculation and alt text support.

mj-button

CTA button rendered as a <table>-based structure for universal client support.

mj-divider

Horizontal line. More reliable than <hr> across email clients.

mj-spacer

Empty vertical space. Use instead of margin for consistent spacing.

mj-raw

Raw HTML passed through untouched: Outlook conditional comments, VML, template tags.

A complete MJML email template

welcome.mjml
42 lines
<mjml>
  <mj-head>
    <mj-attributes>
      <mj-all font-family="Arial, sans-serif" />
      <mj-text font-size="14px" color="#333333" line-height="1.6" />
    </mj-attributes>
  </mj-head>
  <mj-body background-color="#f6f6f6">
    <mj-section background-color="#ffffff" padding="40px 30px">
      <mj-column>
        <mj-image
          src="https://example.com/logo.png"
          width="120px"
          alt="Company Logo"
        />
        <mj-text font-size="24px" font-weight="bold" padding-top="24px">
          Welcome to our platform
        </mj-text>
        <mj-text>
          We're glad you're here. Here's what you can do next:
        </mj-text>
        <mj-button
          background-color="#000000"
          color="#ffffff"
          href="https://example.com/dashboard"
          border-radius="6px"
          font-size="14px"
        >
          Go to Dashboard
        </mj-button>
      </mj-column>
    </mj-section>

    <mj-section padding="20px 30px">
      <mj-column>
        <mj-text font-size="12px" color="#999999" align="center">
          You received this because you signed up at example.com
        </mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

mj-head: defaults, classes, styles and preview text

Everything in <mj-head> shapes the output without appearing in it:

  • mj-attributes sets defaults: mj-all for every tag, a tag name such as mj-text for that tag, and mj-class for a named set you apply with mj-class="name".
  • mj-style adds CSS. With inline="inline" MJML inlines it onto the elements; without, it stays in a <style> block.
  • mj-title and mj-preview set the document title and the preview text shown after the subject line.
  • mj-breakpoint sets the width below which columns stack. The default is 480px.
mj-head
15 lines
<mj-head>
  <mj-title>Your order has shipped</mj-title>
  <mj-preview>Track your parcel in one click</mj-preview>
  <mj-breakpoint width="480px" />
  <mj-attributes>
    <mj-all font-family="Arial, sans-serif" />
    <mj-class name="muted" color="#999999" font-size="12px" />
  </mj-attributes>
  <mj-style inline="inline">
    .footer-link { color: #999999; }
  </mj-style>
</mj-head>

<!-- later, in the body -->
<mj-text mj-class="muted">You received this because you ordered.</mj-text>

Multi-column layouts and mj-group

MJML's column system handles responsive stacking automatically. On desktop, columns sit side by side. Below the breakpoint, they stack vertically. You don't write the media queries. When two columns must stay side by side on a phone, such as an icon next to its label, wrap them in <mj-group>.

Columns
38 lines
<!-- Two-column layout, MJML handles the table math -->
<mj-section>
  <mj-column>
    <mj-image src="https://example.com/feature-1.png" alt="Feature 1" />
    <mj-text font-weight="bold">Fast Setup</mj-text>
    <mj-text>Get started in under 5 minutes.</mj-text>
  </mj-column>
  <mj-column>
    <mj-image src="https://example.com/feature-2.png" alt="Feature 2" />
    <mj-text font-weight="bold">Full Testing</mj-text>
    <mj-text>Test across 21 email clients.</mj-text>
  </mj-column>
</mj-section>

<!-- Three-column layout, each column is automatically 33.33% -->
<mj-section>
  <mj-column>
    <mj-text align="center">Plan A</mj-text>
  </mj-column>
  <mj-column>
    <mj-text align="center">Plan B</mj-text>
  </mj-column>
  <mj-column>
    <mj-text align="center">Plan C</mj-text>
  </mj-column>
</mj-section>

<!-- Side by side on every screen: wrap the columns in mj-group -->
<mj-section>
  <mj-group>
    <mj-column width="50%">
      <mj-text>Left</mj-text>
    </mj-column>
    <mj-column width="50%">
      <mj-text>Right</mj-text>
    </mj-column>
  </mj-group>
</mj-section>

For the strategy behind stacking and fluid layouts, see the responsive email design guide.

Raw HTML, Outlook code and partials: mj-raw and mj-include

<mj-raw> passes its content through without escaping it: Outlook conditional comments, VML, or template syntax such as Handlebars, Liquid or Jinja. <mj-include> pulls a shared partial, such as a header or footer, into the template.

mj-raw and mj-include
8 lines
<mj-raw>
  <!--[if mso]>
  <p style="margin:0;">Shown only in Outlook for Windows</p>
  <![endif]-->
</mj-raw>

<!-- Reuse a partial (MJML 5: includes are off by default) -->
<mj-include path="./partials/footer.mjml" />

In MJML 5, includes are disabled by default for security. Turn them on with --config.allowIncludes true on the command line, or ignoreIncludes: false when calling mjml2html(). For writing the conditional comments themselves, see MSO coding for Outlook.

MJML validation: strict, soft and skip

MJML checks your markup for unknown tags and for each tag's allowed children, attributes and attribute values. The validation level decides what happens to the errors: soft, the default, compiles anyway and returns the errors alongside the HTML; strict stops the build on the first invalid template; skip does not validate. Use strict in CI so a typo in an attribute name fails the build instead of shipping silently.

MJML vs React Email

Both compile to email-safe HTML, but they suit different workflows:

AspectMJMLReact Email
SyntaxCustom XML-like markupJSX (React components)
LogicTemplate literals or external templatingFull JavaScript/TypeScript
ResponsiveBuilt-in (auto stacking)Manual (you handle it)
Best forMarketing emails, designersTransactional emails, React teams

If your team already writes React, the React Email guide covers that route. You can also compare alternatives.

Testing MJML emails across clients

MJML's output is built for compatibility, but no compiler can guarantee the same result in every client. Outlook's Word rendering engine, Gmail's CSS stripping, and Apple Mail's dark mode all introduce edge cases, and anything you add through mj-raw or mj-style is yours to check.

Emailens accepts MJML source directly. It compiles, transforms per-client, and generates a compatibility report with MJML-specific fix suggestions. For the full pre-send routine, see how to test HTML emails.

Questions

How does MJML handle responsive design across email clients?

MJML abstracts table-based responsive layouts behind tags like <mj-section> and <mj-column>. When compiled, it generates conditional MSO tables for Outlook and media queries for mobile clients, so columns sit side by side on desktop and stack below the breakpoint (480px unless you set <mj-breakpoint>).

How do I stop MJML columns stacking on mobile?

Wrap the columns in <mj-group> inside the <mj-section>. Columns in a group stay side by side at every width, so keep their content narrow enough to fit a phone screen.

Do I need to manually inline CSS when using MJML?

No. The MJML engine automatically inlines styles declared inside <mj-attributes> and <mj-style inline='inline'> directly onto elements during compilation, ensuring Gmail and mobile clients receive inline CSS.

Can I include custom HTML or Outlook VML inside MJML?

Yes. The <mj-raw> tag allows you to inject arbitrary raw HTML, conditional comments (like <!--[if mso]>), or template syntax (Handlebars, Liquid, Jinja) directly into the generated output without MJML escaping it.

What is the difference between MJML and React Email?

MJML is a domain-specific XML markup language with built-in layout calculations that works across any backend language. React Email uses JSX and TypeScript within Node/React ecosystems, giving developers component composition and code sharing with web apps.

Your next question

Also useful: HTML email checker · Lint MJML templates in 21 clients · Free HTML email templates, tested in 21 clients

Paste the email that broke.

The checker needs a computer screen.

Get new email CSS fixes by email, or follow by RSS.