npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@emailens/cli

v0.13.17

Published

CLI tool for email compatibility analysis: lint, preview and export HTML, MJML, Maizzle and React Email rendering across 21 email clients.

Readme

The email rendering linter for terminal and CI

CI npm MCP

$ emailens lint emails/welcome.html --fail-on-warning
emails/welcome.html
  warn   7:10  outlook-windows-legacy  display:flex            Outlook Classic receives this flex box. The MSO branch needs a table.
  warn   7:25  outlook-windows-legacy,yahoo-mail,yahoo-mail-android+2  gap                     Outlook Classic does not support "gap".
  warn   8:32  gmail-web,outlook-windows-legacy,yahoo-mail-android+2  box-shadow              Gmail does not support "box-shadow".
  warn   14:5  yahoo-mail          font-family             Yahoo Mail drops an inline font-family that names a quoted font, like 'Courier New': that text shows in Yahoo's own font instead.
  warn         inboxPreview        preheader-too-short     Preheader is only 21 characters, so email clients may backfill with body text (recommended: 30+).
  12 info: safe fallbacks, not problems. --info lists them.

1 file | 5 warnings

Failed: 5 warnings found (--failOnWarning)
See it rendered in Gmail, Outlook and Apple Mail, no signup: https://app.emailens.dev/try?utm_source=cli&utm_medium=lint

That is a welcome email with a flex row and a card shadow, checked against 21 email clients. Each finding has its line:column, and the exit code (here 2) fails the build. The card's rounded corners are among the 12 info findings: Outlook Classic draws them square, a safe fallback rather than a problem. The last line appears only when the check fails; --quiet drops it, and --json never has it.

Install

npm install -g @emailens/cli

Run

emailens lint 'emails/*.html'

Or without installing: npx @emailens/cli lint email.html. HTML, React Email (.tsx/.jsx), MJML and Maizzle are detected from the extension and compiled first, so the check runs on the HTML that would be sent. No account and no network: the analysis runs locally.

Exit codes: 0 clean, 1 errors, 2 warnings with --fail-on-warning, or more than --max-warnings.

Why not caniemail, or React Email's own checker?

Can I email is the support table: one feature, every client. It does not read your email. This CLI's support data is generated from caniemail's (synced 2026-10-27), plus the cells a real-device screenshot contradicted, each marked "Observed" with its date. What the CLI adds is the reading: it finds the features your file uses, points at the line, scores each client, and exits non-zero.

React Email 4 added a compatibility checker, also powered by Can I email, to its preview. Its CLI (email dev, build, start, export) has no check command and no exit code for CI, per its docs. This CLI reads React Email files as well as MJML, Maizzle and plain HTML, and fails the build.

Beyond CSS support, lint checks what neither covers: spam signals, links, accessibility, images, the inbox preview text, size, template variables, overflow, dark-mode and mobile contrast, Outlook VML, styles a client drops, and client-targeting hacks.

Extras

lint options

| Flag | Alias | Description | |------|-------|-------------| | --format | -f | Input format: html, jsx, mjml, maizzle. .vue is maizzle. A pasted Vue file with no flag is detected. | | --json | | Output as JSON, one line, every finding including info | | --info | | List info findings too. By default they are counted, not listed: they are safe fallbacks, not problems | | --fail-on-warning | | Exit 2 if warnings found | | --max-warnings | | Fail if more than n warnings | | --reporter github | | Also print GitHub Actions annotations, so each finding shows on its line in the pull request. Not with --json | | --quiet | -q | No line about the hosted preview after a failure | | --skip | | Comma-separated checks to skip: spam,links,accessibility,images,compatibility,inboxPreview,size,templateVariables,overflow,visual,darkContrast,mobileContrast,design,vml,styleSurvival,targeting | | --targeting-policy | | progressive (default), strict, or lenient |

A .emailensrc in the project, the file the VS Code extension reads, can set rule severities and skips; --skip on the command line wins.

Maizzle HTML needs @maizzle/framework@5 installed beside the CLI. A .vue file needs @maizzle/framework@6. One install is one major.

Other commands

emailens analyze email.html                    # score per client, findings by client
emailens analyze email.html --clients gmail-web,outlook-windows --json
emailens audit email.html                      # compatibility plus every other check
emailens preview email.html --dark-mode        # transforms, analysis, dark mode simulation
emailens preview email.html --screenshots --out ./shots  # needs BROWSERLESS_URL and playwright-core
emailens export email.html -o ./report         # self-contained HTML report (--json for JSON)
emailens fix email.html -o fixed.html          # AI fix, needs ANTHROPIC_API_KEY (--estimate: tokens only)
emailens clients                               # the 21 clients
cat email.html | emailens analyze -            # stdin

emailens inbox wait

The CI step for what your app actually sends. Make your app send a real email (sign-up, reset, receipt) to your Emailens test inbox, then wait for it. It fails when the email is no longer the one you checked in Emailens, or changed since it last arrived: a link moved to staging, an emptied variable, a new line, an image gone, a size jump, signing that stopped passing. Codes, tokens and tracking ids differ on every send and do not count. It needs an API key.

export EMAILENS_API_KEY=...            # app.emailens.dev/dashboard/api
SINCE=$(date +%s000)                   # before the send
npm run send-signup-email              # your app sends it
emailens inbox wait --subject "Welcome" --since "$SINCE"

Options: --subject, --sender, --inbox <id>, --since (ISO time, epoch ms, or a duration such as 5m; default 2 minutes back), --timeout (seconds, default 60, at most 600), --fail-on all|check|drift|none (default all), --json. Exit codes: 0 pass, 1 changed, 2 nothing arrived or Emailens could not be reached.

In your tests

The same check as lint, as an assertion. It throws with every failing finding and its line, reads your .emailensrc, and works in Vitest, Jest and bun:test.

import { render } from "@react-email/render";
import { expectEmailToPass } from "@emailens/cli/test";

test("welcome email passes", async () => {
  expectEmailToPass(await render(<Welcome name="Ada" />));
});

Options: failOnWarning, skip (the --skip names), framework.

In GitHub Actions

- uses: actions/setup-node@v4
  with:
    node-version: 22
- run: npx -y @emailens/cli lint 'emails/*.html' --fail-on-warning --reporter github

--reporter github puts each finding on its line in the pull request's changed files.

** globs are not supported: use a single-level wildcard or list the files. For scores per client in a pull request comment, use the Action.

Hosted

app.emailens.dev/try renders the email per client: five clients with no account, all 21 with a free one. The + Real plans add real-device screenshots: Outlook on Windows and Mac, Apple Mail on Mac and iPad, and Gmail, Yahoo and Proton on the web.

Ecosystem

| Project | What it is | |---|---| | @emailens/engine | The analysis engine this CLI runs, as a library | | @emailens/mcp | The same checks for coding agents: claude mcp add emailens -- npx -y @emailens/mcp | | emailens/action | GitHub Action: fails the build, and can comment the scores per client on the pull request | | emailens/vscode | VS Code extension: findings and preview in the editor |

License

MIT