Skip to main content
Emailens
For HTML email

You wrote the HTML.
21 clients rewrite it.

Emailens reads your .html files and tells you, line by line, what Gmail, Outlook and 19 other clients will do to them. Hand-coded, exported from a builder or rendered by your app: one command, runs locally, no account.

bash
npm install -D @emailens/cli

npx emailens lint 'emails/*.html'

What a flex row looks like in real Outlook

The same CSS on real installs: the client that drops it, and one that renders it as written. This is the gap a browser preview never shows.

Outlook Classic

caniemail: display:flex is not supported in Outlook Classic.

  • Outlook 2019 (Windows), real install · counted as Outlook Classic

    The Emailens probe for display:flex on a real Outlook 2019 (Windows)
More versions (2)
  • Outlook 2016 (Windows), real install · counted as Outlook Classic

    The Emailens probe for display:flex on a real Outlook 2016 (Windows)
  • Outlook 2007 (Windows), real install · counted as Outlook Classic

    The Emailens probe for display:flex on a real Outlook 2007 (Windows)

Emailens simulation of Outlook Classic

The same probe in the Emailens simulation of Outlook Classic
Outlook 365

caniemail: display:flex works in Outlook 365.

  • Outlook.com, real install · counted as Outlook 365

    The Emailens probe for display:flex on a real Outlook.com

Emailens simulation of Outlook 365

The same probe in the Emailens simulation of Outlook 365

This Outlook.com install drops the <style> in the head, so only inline styles apply.

Every account includes one free real-device check on 4 clients, after you verify your email and run one preview, while this month's free capacity lasts. How real-device checks work →

Findings with a line, a client and a reason

This is an excerpt of the output for a hand-written welcome email: a web font, a flex row, a rounded button and a CSS background hero. The command exits non-zero when it finds an error.

text
emails/welcome.html
  error   6:1   gmail-web,gmail-android,gmail-ios+14   <link>               Gmail does not support external stylesheets.
  error         spam                                   missing-unsubscribe  No unsubscribe link found; required by CAN-SPAM and GDPR.
  warn    9:10  outlook-windows-legacy                 display:flex         Outlook Classic receives this flex box. The MSO branch needs a table.
  warn    9:25  outlook-windows-legacy,yahoo-mail+3    gap                  Outlook Classic does not support "gap".
  warn   10:64  outlook-windows-legacy                 padding              Outlook Classic drops this padding. Put it on a table cell, or set mso-padding-alt on one.
  warn   11:11  outlook-windows-legacy                 background-image     Outlook Classic drops this background image, and no v:fill covers it.

The gap between your preview and the inbox

Plain HTML has no framework smoothing anything over: every property reaches every client as written. These are findings from the same welcome email.

<link> web font

Gmail and 14 other clients drop external stylesheets, so the font never loads there and readers see the fallback in font-family.

display:flex

Outlook Classic renders with Word, which has no flexbox: the columns stack or collapse unless an MSO branch gives it a table.

background-image

Outlook Classic drops CSS backgrounds. Without a VML fill and a background-color fallback, the hero is a blank block there.

padding

Outlook Classic drops padding on this element. On a table cell, or with mso-padding-alt, it holds.

The email as a whole

Missing unsubscribe link, missing physical address, images without alt text, a missing title: the things spam filters, screen readers and inbox previews look at.

Block the pull request that breaks Outlook

The same command, on the runner. No API key, no quota, no network call to Emailens.

One thing to know first: lint the HTML you send

bash
# best: one real email your app rendered
npx emailens lint 'dist/emails/*.html'

# a raw template with {{placeholders}}: skip the variable check
npx emailens lint 'templates/*.html' --skip templateVariables

If your app fills a template at send time, render one real email to a file and lint that: conditional blocks and injected content are part of what clients see. Linting the raw template works too, with the template-variable check skipped.

GitHub Actions

email-lint.yml
# .github/workflows/email-lint.yml
name: Email lint
on:
  pull_request:
    paths: ["emails/**"]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npx emailens lint 'emails/**/*.html'

Your AI writes the email. Emailens checks it.

Add the MCP server and Claude Code or Cursor can lint the template it just wrote, read the findings, and fix them before you review the diff.

$ claude mcp add emailens -- npx -y @emailens/mcp

Common questions

Do I need a framework to use Emailens? +

No. Plain HTML is the default input. React Email, MJML and Maizzle are compiled to HTML first; an .html file is checked as it is, with a line and column on each finding.

Do I need an account or an API key? +

No. The CLI runs the open-source @emailens/engine on your machine or your CI runner. There is no quota and nothing is uploaded. An account is only needed for hosted screenshots, share links, and the REST API.

Can I lint an email exported from a builder or an ESP? +

Yes. Save the exported HTML to a file and point the command at it. The findings refer to that file, so you can fix the export or the template it came from.

Does it fix the HTML for me? +

The CLI reports; it does not rewrite your file. Each finding names the property and the client, and the hosted preview's AI Fix proposes an edited version you can review.

How is the score calculated? +

Each client gets a score from 0 to 100. The headline score is the worst client, not an average, because an email that renders in twenty clients and breaks in Outlook is a broken email.

Lint your next template

Runs locally. No signup required.

$ npx emailens lint 'emails/*.html'

Using something else? Lint React Email templates or Lint MJML templates or Lint Maizzle templates

New to HTML email? HTML Email: How to Code One That Works in Every Inbox