Skip to main content
Emailens
For React Email

React Email writes the HTML.
21 clients rewrite it.

Emailens lints the HTML your components export and tells you what Outlook, Gmail and 19 other clients will do to it. Two commands. Runs locally. No account.

bash
npm install -D @emailens/cli

npx email export --outDir out --pretty
npx emailens lint 'out/*.html'

Findings with a client, a line, and a reason

This is the output for a template built from Container, Button and Tailwind. The command exits non-zero when it finds an error.

text
out/welcome.html
  error          spam             missing-unsubscribe  No unsubscribe link found; required by CAN-SPAM and GDPR.
  warn    37:47  protonmail       text-decoration      Proton Mail does not support "text-decoration".
  warn   37:124  outlook-windows  border-radius        Outlook (New) does not support "border-radius".

The gap between your preview and the inbox

React Email's components are built for compatibility, and the preview server still renders in a browser. These are findings from a template built with stock components.

border-radius

Outlook on Windows does not support it, so a rounded <Button> ships square unless it has a VML fallback.

max-width

Outlook only honours max-width on a table it actually reads. A centered 600px layout is the usual casualty.

padding

Outlook Classic keeps padding on table cells only. Spacing set anywhere else is dropped.

text-decoration

Proton Mail does not support it, and it is what removes the underline from a button or link.

The email as a whole

Missing unsubscribe link, missing physical address, empty <title>, preheader too short: the things spam filters and inbox previews read.

Block the pull request that breaks Outlook

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

One thing to set up first: default props

tsx
// email export renders with no props, and does not apply PreviewProps.
export const Welcome = ({ name = "Ada" }: Partial<Props>) => (
  // ...
);

A prop with no default makes email export throw before Emailens ever sees the HTML.

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 email export --outDir out --pretty
      - run: find out -name '*.html' -print0 | xargs -0 -n1 npx emailens lint

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

Does Emailens replace React Email? +

No. React Email is how you write and render the email. Emailens checks the HTML it produces against 21 email clients. You keep your components, your preview server, and your sending provider.

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 the .tsx files directly? +

Only self-contained ones. Direct compilation runs your component in a sandbox that allows imports from react and @react-email/components and nothing else, so a template that imports a shared footer or a helper will not compile. Linting the output of email export has no such limit, which is why it is the recommended path.

Why does email export fail on my template? +

email export renders each template with no props and does not apply PreviewProps. If a component reads a prop that has no default value, the render throws. Give every prop a default in the function signature.

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.

Does it work with Tailwind in React Email? +

Yes. The <Tailwind> component inlines its classes when React Email exports the HTML, and Emailens lints that inlined output like any other email.

Lint your next template

Runs locally. No signup required.

$ npx email export --outDir out --pretty && npx emailens lint 'out/*.html'

Using something else? Lint MJML templates or Lint Maizzle templates