> ## Documentation Index
> Fetch the complete documentation index at: https://docs.allgoodhq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How MJML works

> What MJML is, why email HTML needs a compiler, and how allGood turns MJML tags into Outlook-safe markup.

**MJML** is a markup language for email. You write a few high-level tags, and a compiler turns them into the table-based HTML that inboxes need. Every allGood [Email Component](https://docs.allgoodhq.com/mk/content/email/components/index.md) and Email Template is MJML source.

This article covers what MJML is and what the compiler does for you. To compare it with Marketo, HubSpot, or hand-coded HTML, see [Compare MJML with other email tools](https://docs.allgoodhq.com/mk/developer/email/compare-mjml-with-other-tools.md).

## Why email HTML needs a compiler

Email clients do not render HTML the way browsers do. Outlook has used Word's rendering engine since 2007. It ignores most modern CSS and expects layouts built from tables. A layout that holds up in Outlook, Gmail, and Apple Mail takes long, repetitive markup. You end up solving the same problems in every template, such as a responsive two-column row or a button that survives Outlook.

MJML moves that work into the tags. You describe the layout once, and the compiler adds the workarounds each client needs.

## What an MJML file looks like

An MJML file is XML. The `<mjml>` root holds one `<mj-head>` for metadata, such as the title, preview text, and shared style defaults. It also holds one `<mj-body>`, which is the email itself. The body is built from about thirty tags. These five cover most layouts:

| Tag | What it does |
| - | - |
| `mj-section` | A horizontal row. It is the main structural unit. |
| `mj-column` | A vertical slice of a section, side by side with its siblings. |
| `mj-text` | Any text content. |
| `mj-button` | A call-to-action link, built to survive Outlook. |
| `mj-image` | An image. |

A minimal example:

```xml theme={null}
<mjml>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text font-size="24px">Hello</mj-text>
        <mj-button href="https://example.com">Read more</mj-button>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>
```

Compiling it produces nested tables, inline styles, and the conditional markup Outlook needs. This is an abbreviated version. The real output is longer.

```html theme={null}
<table role="presentation" width="100%">
  <tr>
    <td>
      <table role="presentation" width="100%">
        <tr>
          <td style="font-size:24px;">Hello</td>
        </tr>
        <tr>
          <td>
            <table role="presentation"><tr>
              <td style="background:#414141; border-radius:3px;">
                <a href="https://example.com" style="display:inline-block; padding:10px 25px;">Read more</a>
              </td>
            </tr></table>
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>
```

You never write the second block by hand. The compiler generates it fresh from the `.mjml` source each time.

## What the compiler handles for you

Each of these is a workaround you would otherwise remember and repeat in every email:

| Hand-coded technique | Why it exists | What MJML does |
| - | - | - |
| Nested `<table role="presentation">` layout | Outlook only supports table layouts reliably. | `mj-section` and `mj-column` generate it. The `role` also stops screen readers from announcing layout as data. |
| VML `<v:roundrect>` button fallback | Outlook's Word engine ignores `border-radius`. | `mj-button` generates it. |
| `<!--[if mso]>` conditional comments | They target Outlook-only markup. | Generated in the compiled output. |
| Inline `style` attributes | Many clients strip `<style>` blocks from the `<head>`. | Styles are inlined for you. |
| Spacer rows and ghost tables | Clients handle margin inconsistently. | Use `padding` on any component. |
| Media queries for stacking | Columns must stack on mobile. | Columns stack at a breakpoint you set. The default is 480px (`mj-breakpoint`). |

The trade-off is that you can only build what the component set expresses. For anything else, such as a tracking pixel or custom markup, MJML has one escape hatch: `<mj-raw>`. It passes content through untouched. Nothing inside it is validated, and none of the workarounds apply. Using it puts you back in hand-coding for that piece.

## The compile step in allGood

In allGood, `template.mjml` is never sent anywhere. `template.html` is a build artifact made by running a render script on the MJML source:

```bash theme={null}
node render-mjml.mjs --dir my-template.ag-email-template --in template.mjml --out template.html
```

Liquid citations such as `{{ asset.copy.content.headline.value }}` are filled in later, at send time, against the compiled HTML. Personalization is a separate job from layout. MJML only handles layout.

![The hero block's MJML source compiles to this HTML, with doctype, MSO conditional comments, and inline reset styles included. Nobody hand-writes it.](https://mintcdn.com/allgoodtechnologyinc/vWyFubp1-YTCUXDt/images/mk/content/email/components/hero-compiled-html.png?fit=max\&auto=format\&n=vWyFubp1-YTCUXDt\&q=85\&s=096bdd9acc67462620128e1569c733ad)

The hero block's MJML source (left) compiles to this HTML. It includes the doctype, MSO conditional comments, and inline reset styles. Nobody writes the right side by hand.

> [!WARNING]
>
> Never write `template.html` by hand. The render step proves your MJML is valid and your Liquid citations survived the compile. Skip it, and you can publish a template whose HTML does not match its source.

> [!NOTE]
>
> Put a Liquid condition around a whole `mj-section`, never around one `mj-column` in a multi-column section. MJML fixes column widths at render time, before Liquid removes anything. The surviving column keeps its half width and leaves a blank gap. Nothing errors, so it only looks broken in the inbox.

## Do you have to write MJML?

No. allGood's AI can convert an existing HTML email, a Marketo template, or a HubSpot module into MJML for you. Review the result before you treat it as finished. Writing components directly is for designers and developers who want full control. See [Write an email component](https://docs.allgoodhq.com/mk/developer/email/how-to-write-email-components.md).

## MJML's own resources

MJML is open source and documented well beyond this article:

* [MJML: getting started](https://mjml.io/getting-started-onboard) is an interactive tutorial for the same tag-based idea.
* [The MJML guide](https://documentation.mjml.io/#mjml-guide) is the full reference. It covers every component and installation option.
* [MJML Live](https://mjml.io/try-it-live) is an online editor. Paste MJML on one side to see the compiled HTML and a preview on the other.
* [MJML community](https://mjml.io/community) lists editor plugins, framework integrations, and tutorials.

## Related articles

* [Compare MJML with other email tools](https://docs.allgoodhq.com/mk/developer/email/compare-mjml-with-other-tools.md)
* [Write an email component](https://docs.allgoodhq.com/mk/developer/email/how-to-write-email-components.md)
* [Email components](https://docs.allgoodhq.com/mk/content/email/components/index.md)
* [What MJML is](https://docs.allgoodhq.com/mk/content/email/components/what-mjml-is.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.