Email QA Checker

MJML checker

MJML takes care of responsive layouts and Outlook quirks, but it can't tell that a button still points to “#” or an image has no alt text. Compile your MJML, paste the HTML, and see the content problems first.

Check my emailRead the guideFree · no sign-up · your email code is not stored.

Content issues first

Results open filtered to the things MJML can't fix: links, images, tracking, accessibility and copy.

Try real MJML output

The sample is genuine MJML compiler output with the content mistakes we see most often.

MJML-specific fixes

The guide shows which mj- tags and attributes prevent each problem at the source.

Email type

Paste compiled HTML, not .mjml source: run npx mjml email.mjml -o email.html (or copy the HTML from the MJML app or online editor).

Your email code is not stored.

Your QA report will appear here

85 deterministic checks across 8 categories. Paste HTML and press Run QA Check, or load the sample to see a report straight away.

  • Compatibility18
  • Outlook9
  • Accessibility6
  • Links12
  • Images12
  • Mobile5
  • Tracking7
  • HTML Quality16

Guide

How to QA an MJML email before you send it

MJML solves rendering. These seven habits solve the content problems MJML can't see.

What MJML handles — and what it doesn't

MJML compiles simple components (<mj-section>, <mj-column>, <mj-button>) into the nested tables, Outlook conditional comments and responsive CSS that email clients need. That's why compiled MJML rarely trips compatibility checks.

But MJML only knows about structure. It can't tell that a link is a placeholder, an image needs alt text, a UTM value is empty or a TODO is still in the copy. Those mistakes compile straight through — so check the output, not just the source.

Compile, then paste email.html into the checker
# compile
npx mjml email.mjml -o email.html

# log validation errors (exits with code 1 if there are any — useful in CI)
npx mjml -v email.mjml

# or refuse to compile invalid MJML
npx mjml -l strict email.mjml -o email.html

1. Set the language on <mjml>

Unless you set it, recent MJML versions output lang="und" (“undetermined”), so screen readers have to guess the language. Set it once on the root tag:

HTML
<mjml lang="en" dir="ltr">

<mj-title> sets the document title (shown in browser views and announced by some screen readers). <mj-preview> sets the preheader — the preview text inbox lists show next to your subject. Without it, clients show whatever text comes first, often “View in browser” or alt text.

HTML
<mj-head>
  <mj-title>Join us for the autumn meetup</mj-title>
  <mj-preview>Talks, demos and drinks with the team on 24 October. Save your seat.</mj-preview>
</mj-head>

3. Give every mj-image an alt

If you leave out alt, the compiled image ends up with no useful alt text. On a linked image that's a link with no name for screen-reader users, and an empty box when images are blocked. Describe what the image says or where it leads; use alt="" only for purely decorative images.

HTML
<mj-image src="https://cdn.acme.com/email/autumn-meetup.jpg"
          href="https://acme.com/events/autumn-meetup"
          alt="Autumn community meetup, 24 October" />

4. Give every mj-button a real href

Templates often start with href="#", and it's easy to miss when the button looks finished. A button without a real destination does nothing for the people most ready to click. Check every <mj-button> and linked <mj-image> before you compile for the last time.

HTML
<mj-button href="https://acme.com/events/autumn-meetup/rsvp?utm_source=newsletter&utm_medium=email&utm_campaign=autumn-meetup">
  Save my seat
</mj-button>

5. Keep tracking consistent

Long MJML files make it easy to tag one link and forget the next. Use the same utm_source, utm_medium and utm_campaign on every link, never leave a value empty, and don't hard-code UTMs if your email platform already adds them. The link and UTM guide covers this in detail.

6. Watch the size: Gmail clips at about 102 KB

MJML output is verbose, and Gmail hides everything after roughly 102 KB of HTML behind a “View entire message” link — often including your footer, unsubscribe link and open tracking. To stay well under it:

  • Set shared styles once with <mj-attributes> and <mj-class> instead of repeating attributes on every component.
  • Minify the compiled HTML in your build step, making sure the minifier keeps Outlook conditional comments.
  • Don't embed images as base64 — host them.
  • Leave headroom: your email platform adds tracking code after you upload.
HTML
<mj-attributes>
  <mj-all font-family="Helvetica, Arial, sans-serif" />
  <mj-text font-size="16px" line-height="24px" color="#18181b" />
  <mj-button background-color="#4f46e5" border-radius="6px" font-weight="bold" />
</mj-attributes>

7. Be careful with mj-raw

Anything inside <mj-raw> is passed through untouched. That's useful for custom snippets, but MJML can't protect you there: forms, scripts, position or flexbox inside <mj-raw> will break in the same clients they always do. Paste the compiled output into the checker and it will catch those too.

A simple MJML QA workflow

  1. Build in MJML with lang, <mj-title>, <mj-preview> and shared <mj-attributes>.
  2. Validate (mjml -v) and compile.
  3. Paste the compiled HTML into the checker and fix what it finds in your .mjml source.
  4. Send a test to yourself, click every link and read it with images off.

What we check

85 deterministic rules — no AI guesswork. Confirmed problems are errors; patterns that only break in some clients are warnings.

Links

12 checks

Empty, # and malformed hrefs, localhost and example.com leftovers, mailto: and tel: formatting.

  • –Empty link
  • –Placeholder link href="#"
  • –Localhost or private-network link
  • –Staging or test link
  • –example.com or placeholder domain
  • –Placeholder link text in href
  • –Relative link URL
  • –Malformed URL
  • –Non-HTTPS link
  • –Same URL linked multiple times
  • –Email address link without mailto:
  • –Phone link formatting

Images

12 checks

Alt text, width/height attributes, HTTPS, relative paths, base64 and placeholder images.

  • –Image without src
  • –Missing alt attribute
  • –Empty alt on a meaningful image
  • –Non-descriptive alt text
  • –Image missing width
  • –Image missing height
  • –Oversized image dimensions
  • –Image loaded over HTTP
  • –Relative image path
  • –Placeholder or local image
  • –Base64 embedded image
  • –Images without display:block

Accessibility

6 checks

lang, titles, readable font sizes and links that make sense to screen-reader users.

  • –Missing lang attribute
  • –Missing <title>
  • –Very small font size
  • –Very large font size
  • –Link with no accessible text
  • –Vague link text

Tracking

7 checks

UTM coverage and consistency, empty campaign values, duplicate and malformed parameters.

  • –No UTM tracking on any link
  • –Link missing UTM parameters
  • –Incomplete UTM set
  • –Inconsistent UTM values
  • –Empty or placeholder campaign value
  • –Duplicate query parameter
  • –Malformed query string

FAQ

Can I paste raw MJML?
Not yet. The checker detects MJML and asks for the compiled HTML, because that's what email clients actually receive. Native MJML checking is on the roadmap.
Why does compiled MJML pass most compatibility checks?
MJML generates the tables, Outlook conditional comments and responsive CSS that email clients need, and the checker recognises those patterns instead of flagging them.
See all FAQs