HTML vs. MJML: What's Actually in Your Email
Email HTML is a nightmare that hasn't improved since 2004. MJML exists so you don't have to deal with it. Here's what that means in practice.
If you've ever hit "view source" on a marketing email, you've seen the horror. Nested tables six levels deep. Inline styles on every element. Conditional comments for Outlook. mso- prefixed CSS properties that target a rendering engine from 2007.
This is what email HTML looks like. And there's a reason for it.
Why email HTML is stuck in 2004
Web browsers moved on. Email clients didn't. Here's why:
Outlook uses Microsoft Word's rendering engine. Not a browser engine. Not WebKit. Not Gecko. Microsoft Word. This has been the case since Outlook 2007, and it's still true in Outlook for Windows in 2026. Word's HTML rendering supports a subset of CSS that was incomplete even by 2004 standards.
Gmail strips your <style> tags (mostly). It inlines your styles and removes anything it considers risky, which includes a lot of modern CSS. Animations, custom properties, calc(), and most pseudo-selectors are gone.
Apple Mail is actually pretty good — it uses WebKit, the same engine as Safari. But you can't design for just Apple Mail unless you've verified that 100% of your audience uses it. (They don't.)
The practical consequence: email HTML has to be written for the worst common denominator. That means:
- Tables for layout. Not CSS Grid. Not Flexbox.
<table>,<tr>,<td>. The same technique web developers used before CSS layout existed. - Inline styles everywhere. Because external and embedded stylesheets get stripped or ignored by various clients.
- Conditional comments for Outlook.
<!--[if mso]>wrapping blocks of code that only Outlook sees, to fix things that only Outlook breaks. - No semantic HTML.
<article>,<section>,<header>— these mean nothing to most email clients. You're back to<div>and<table>.
What MJML does and why it exists
MJML (Mailjet Markup Language) is an abstraction layer. You write simple, readable markup, and MJML compiles it into the horrifying-but-functional HTML that email clients need.
Think of it like this:
- You write: A clean component tree with tags like
<mj-section>,<mj-column>,<mj-text>,<mj-button>. - MJML outputs: The nested tables, inline styles, Outlook conditionals, and responsive media queries that make it all work.
MJML handles:
- Responsive layout. Columns stack on mobile automatically. You don't write media queries.
- Outlook compatibility. The compiled output includes all the
mso-hacks and conditional comments. - Consistent spacing. Padding and margins work predictably across clients.
- Font fallbacks. Web fonts degrade gracefully.
A side-by-side: the same email in raw HTML vs MJML
Here's what a simple two-column section looks like in both formats.
The MJML version
<mj-section background-color="#ffffff">
<mj-column>
<mj-image src="product.jpg" alt="Product" />
</mj-column>
<mj-column>
<mj-text font-size="20px" font-weight="bold">
New Product Launch
</mj-text>
<mj-text>
Introducing our latest thing. It does stuff.
</mj-text>
<mj-button background-color="#4f46e5">
Learn More
</mj-button>
</mj-column>
</mj-section>
13 lines. Readable. Obvious what each piece does.
The compiled HTML output
<!--[if mso | IE]>
<table align="center" border="0" cellpadding="0"
cellspacing="0" role="presentation"
style="width:600px;" width="600">
<tr>
<td style="line-height:0;font-size:0;
mso-line-height-rule:exactly;">
<![endif]-->
<div style="background:#ffffff;background-color:#ffffff;
margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0"
cellspacing="0" role="presentation"
style="background:#ffffff;background-color:#ffffff;
width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0;
padding:20px 0;text-align:center;">
<!--[if mso | IE]>
<table role="presentation" border="0"
cellpadding="0" cellspacing="0">
<tr>
<td style="vertical-align:top;width:300px;">
<![endif]-->
<!-- ... another 80+ lines ... -->
100+ lines. For the same two-column section. And this is a simple example.
No human should write this by hand. That's not a dig at HTML developers — it's an acknowledgment that the constraints are absurd and automation is the correct response.
The rendering engine problem
To understand why MJML exists, you need to understand that "email client" is a misleading term. It implies a single target. In reality, you're targeting at least four different rendering engines:
| Engine | Used By | CSS Support |
|---|---|---|
| WebKit | Apple Mail, iOS Mail | Excellent — close to Safari |
| Blink (limited) | Gmail (web, Android) | Moderate — strips <style>, limits selectors |
| Word | Outlook (Windows) | Poor — tables only, limited CSS |
| Gecko (limited) | Thunderbird | Good, but small market share |
Each engine has its own quirks, its own bugs, and its own list of CSS properties it ignores. Writing raw HTML that works in all four means writing code that's essentially four different emails in one file, held together by conditional comments and prayers.
MJML has already solved these problems. Its compiler knows what each engine needs and generates the appropriate hacks.
Why you shouldn't write email HTML by hand
Let's be direct:
- It's slow. A simple layout that takes 5 minutes in MJML takes 30+ minutes in raw HTML, most of that spent on Outlook fixes.
- It's fragile. Change one
<td>width and the whole layout can collapse in one client while looking fine in another. - It's not maintainable. Coming back to email HTML you wrote three months ago is like reading someone else's code — except worse, because the "someone else" was a version of you who was angry at Outlook.
- The bugs are invisible. You won't see Outlook rendering issues unless you test in Outlook. And testing in Outlook means running a Windows machine with a specific version of Office.
The only legitimate reason to write email HTML by hand is if you need pixel-perfect control over something MJML doesn't support. That covers maybe 2% of use cases.
Where ModuLetter fits in
ModuLetter uses MJML under the hood. When you add a Section, a Text block, or a Button in the visual editor, you're creating an MJML component tree. When you export, ModuLetter compiles that tree into production-ready HTML.
You get three views:
- Preview — What the email looks like, rendered live.
- HTML — The compiled output, ready to paste into Mailchimp, Klaviyo, or your ESP.
- MJML — The clean source, if you want to inspect or hand-edit it.
You never have to write a <table> tag. You never have to debug an Outlook conditional comment. You never have to wonder why your padding is wrong in Gmail.
The tables are still there — in the compiled output. But they're MJML's problem now, not yours.