Skip to content

guides

Testing email rendering across clients before you hit send

May 7, 2026

#email-testing#email-rendering#email-qa#outlook#dark-mode
Testing email rendering across clients before you hit send
Visual: Bureau Morandi on okslop

Your email looked perfect in the browser preview, so you sent it. Then someone replied with a screenshot from Outlook showing your three-column layout stacked into a single broken column, your logo invisible on a dark background, and your call-to-action button rendered as plain blue text.

To test email rendering across clients before you send, render your template to its final HTML, lint the markup, then do a seed send: deliver the real message to test accounts you own at Gmail, Outlook, Apple Mail, and one mobile client, and inspect each on a real device, including dark mode and with images disabled. You cannot test every client perfectly, and you do not need to. You need to catch the four failures that actually happen in the wild: Outlook's Word rendering engine, dark-mode color inversion, blocked images, and layouts that collapse on narrow screens.

This guide builds that workflow the honest way, from free seed sends up to paid screenshot services, and explains why email rendering is a uniquely miserable corner of frontend work. Then, at the end, the shortcut: how a stack with preview built in lets you skip most of the manual ceremony.

Why is email rendering so much harder than web rendering?

Email rendering is harder than the web because there is no shared, modern rendering engine and no way to ship a fix after send. A web page renders in a handful of browsers that all track current CSS and update continuously, and if you find a bug you push a patch. An email renders in dozens of clients (Gmail web, the Gmail Android and iOS apps, Outlook 2016/2019/365 on Windows, Outlook on Mac, Outlook.com, Apple Mail on macOS and iOS, Yahoo, ProtonMail, Thunderbird, and more), each with its own quirks, none of which you control, and once the message is in someone's inbox you can never change it.

The worst offender is Outlook on Windows, which renders HTML using Microsoft Word's layout engine rather than a browser. Word does not support flexbox, CSS grid, float reliably, background images (without VML), or many properties you use without a second thought. Other clients strip your <style> block, ignore media queries, or rewrite your CSS. Some mobile clients auto-scale fonts. Some invert your colors in dark mode whether you asked or not.

The result is that email HTML in 2026 still looks like web HTML from 2005: nested tables, inline styles, fixed pixel widths, and conditional comments. You are not writing bad code; you are writing to the lowest common denominator on purpose, because the lowest common denominator is a huge fraction of your recipients.

What email clients should I actually test against?

Test against the clients your recipients use, and weight your effort by the engines that break things, not by the long tail of clients that mostly behave. The minimum viable test matrix is four buckets:

  • Outlook on Windows (Word engine). The single most likely source of layout bugs. If your email survives Outlook, it survives almost everything.
  • Gmail (web and the mobile app). Huge share of recipients. Gmail strips your <style> head in some contexts, clips long messages with a "view entire message" link past about 102KB, and the app handles dark mode differently from the web.
  • Apple Mail (macOS and iOS). Strong CSS support, but aggressive automatic dark-mode color inversion that catches people out.
  • One Android client. The Gmail Android app or the stock client, to catch mobile-specific scaling and width issues.

Pull your own audience data before assuming. If your analytics or your sending platform reports client usage, test the top clients your recipients actually open in. A B2B SaaS audience skews heavily to Outlook and corporate mail; a consumer newsletter skews to Gmail and Apple Mail on phones. Test where your people are.

You do not need to test ProtonMail, Thunderbird, or Outlook 2007 unless your audience data says you should. Chasing 100% client coverage is a tax with diminishing returns. Cover the four buckets above and you have caught the failures that generate real support tickets.

How do I set up a free seed-inbox test?

Create test accounts at each major provider, send the real message to all of them, and inspect on real devices. This is the cheapest reliable test, and it reflects your actual reputation and rendering instead of a synthetic preview.

Set up once: a Gmail account, an Outlook.com account, a Yahoo account, and an Apple iCloud account, all of which are free. Collect their addresses into a seed list. Then make seed sending part of your workflow, not an afterthought.

Here is a minimal seed-send script that posts your rendered HTML to your sending endpoint for each seed address. This assumes you already have the final HTML in hand (see the next section for generating it):

// seed-send.ts: fire the real message at every seed inbox
const SEEDS = [
  "yourname.seed@gmail.com",
  "yourname.seed@outlook.com",
  "yourname.seed@yahoo.com",
  "yourname.seed@icloud.com",
];

async function seedSend(subject: string, html: string, text: string) {
  for (const to of SEEDS) {
    const res = await fetch("https://api.acme.com/send", {
      method: "POST",
      headers: {
        "content-type": "application/json",
        authorization: `Bearer ${process.env.SEND_TOKEN}`,
      },
      body: JSON.stringify({
        from: "Acme <hello@news.acme.com>",
        to,
        subject: `[SEED] ${subject}`,
        html,
        text,
      }),
    });
    if (!res.ok) {
      console.error(`seed to ${to} failed: ${res.status} ${await res.text()}`);
      continue;
    }
    console.log(`seeded ${to}`);
  }
}

Then open each seed inbox on a real desktop and a real phone and look. Do not trust the desktop web client to predict the mobile app; they render differently. The whole point of a seed send is that it goes through your real sending path, so you also verify authentication, the preheader, and the from-name in passing.

The limitation is honest: four accounts is not forty clients, and you are eyeballing, not diffing screenshots. But it catches the majority of real-world breakage at zero cost, which is exactly the right trade for most transactional and low-volume marketing mail.

How do I generate the HTML I'm supposed to test?

Test the final, inlined HTML that will actually be sent, not your source template, because the build step is where things change. If you author in a framework, the compiled output is what lands in the inbox, and it is what you must inspect.

The two sane ways to author email in 2026 both compile to the table-based, inline-styled HTML that survives Outlook, so you do not hand-write tables:

  • MJML: a markup language that compiles to responsive, Outlook-safe HTML.
  • React Email: write components, render to HTML on a Worker or in a build step. See rendering React Email on Cloudflare Workers for doing this at the edge.

A minimal render-and-inline step looks like this. It compiles the template, inlines the CSS (because Gmail strips <style>), and generates a plaintext alternative so you never ship HTML-only:

// build-email.ts: compile, inline CSS, generate plaintext
import mjml2html from "mjml";
import { convert } from "html-to-text";
import juice from "juice";

export function buildEmail(mjmlSource: string) {
  const { html: rawHtml, errors } = mjml2html(mjmlSource, { validationLevel: "strict" });
  if (errors.length) {
    // Fail loudly: a broken template should never reach a seed send.
    throw new Error(`MJML errors: ${errors.map((e) => e.formattedMessage).join("; ")}`);
  }
  const html = juice(rawHtml); // inline the CSS so Gmail keeps it
  const text = convert(html, { wordwrap: 80 }); // plaintext alternative
  return { html, text };
}

If you skip the inlining step, your email will look fine in your local browser preview and then lose all its styling in Gmail, because Gmail discards the <style> block but keeps inline style= attributes. This is the single most common "it worked in preview" failure. Test the inlined output. For the templating side (variables, conditionals, the data you merge in), see building transactional email templates with variables.

How do I test email dark mode rendering?

Toggle dark mode on each test device and re-inspect, because dark mode is applied by the client after your HTML arrives, and clients do it inconsistently. There is no single dark-mode standard, so a message that looks fine in light mode can be unreadable in dark mode on the same client.

The classic failure is a dark logo on a transparent or white background. Apple Mail and some other clients auto-invert colors in dark mode, so your white email background becomes dark, but your dark-text logo PNG does not invert, leaving dark-on-dark and invisible. Defend against it:

  • Use a logo PNG with padding or a light stroke so it survives on any background, or supply a version designed to read on dark.
  • Set explicit background-color on table cells rather than relying on a default, so clients that respect your colors keep your intended palette.
  • Add the color-scheme meta tags so clients that honor them stop force-inverting:
<meta name="color-scheme" content="light dark">
<meta name="supported-color-schemes" content="light dark">

These tags tell supporting clients (Apple Mail, some Outlook builds) that you have handled both schemes, which reduces aggressive auto-inversion. They are not universally respected, which is why you still test the real message in dark mode rather than trusting the tags.

The test itself is mechanical: open the seed message, switch the device to dark mode, and look. Do it on iOS Apple Mail specifically, because its inversion behavior is the one that surprises people most.

How do I test with images disabled?

Disable image loading in your test client and confirm the message still makes sense from text and alt attributes alone. Many clients block remote images by default until the recipient clicks "load images," so a meaningful fraction of people see your email with no images on first open, every time.

This matters most for image-heavy marketing email. If your call-to-action is a button image with no text fallback, an images-off recipient sees a blank box where the button should be and cannot act. The fixes:

  • Give every <img> descriptive alt text, so a blocked image still communicates.
  • Never make a whole email a single image. Beyond rendering, an image-only email is a spam signal; see the content signals spam filters actually score.
  • Use real HTML text and CSS for buttons (bulletproof buttons) instead of an image, so they render with images off and survive dark mode.

To test, turn off "load remote images" in Gmail settings or Apple Mail preferences, reopen the seed message, and read it as if you were a first-time recipient who has not clicked to load images. If it still reads and the primary action is still clickable, you are fine.

When are Litmus or Email on Acid worth paying for?

Pay for a screenshot service when the cost of a rendering bug reaching your audience exceeds the subscription, which means high-volume campaigns, not every transactional receipt. Litmus and Email on Acid render your message across 40-90 real clients (including legacy Outlook versions and many mobile apps you cannot easily own), return screenshots, and bundle spam-filter and link checks.

The math is straightforward. If you send a newsletter to 200,000 people and a dark-mode bug makes your unsubscribe link invisible, you are looking at spam complaints and a deliverability hit that a paid preview would have caught for the price of a few months' subscription. If you send 500 password-reset emails a day from a simple, single-column template that has not changed in months, a paid service is overkill; a four-account seed send covers you.

The honest limitation of these services: a screenshot is a static render, not a live client, so it will not catch interaction bugs, and it still cannot predict inbox placement, because placement depends on reputation, not rendering. Use them for what they are good at (broad rendering coverage) and keep doing seed sends for the real-path verification they cannot replace.

A pre-send rendering checklist

Run this before any real send. It is the same discipline whether you seed-send for free or pay for screenshots:

  • The HTML you are testing is the final, CSS-inlined output, not the source template.
  • A plaintext text/plain part exists alongside the HTML.
  • The layout uses nested tables and inline styles, no flexbox or grid, so Outlook survives.
  • The message renders sanely at 320px mobile width without horizontal overflow.
  • Dark mode does not invert your logo or text into invisibility; you checked on iOS Apple Mail.
  • Every image has descriptive alt text, and the email reads with images blocked.
  • Primary buttons are HTML/CSS, not images.
  • The subject line and preheader read correctly when truncated on mobile.
  • Links work and point to your own sending domain.
  • You opened the real seed message on at least Gmail, Outlook, Apple Mail, and one mobile client.

If every box is ticked, you have caught the failures that generate support tickets. The long tail of exotic clients is a separate, optional investment. Rendering is only half of a pre-send pass; for the deliverability and content side of the same gate, pair this with the 12-point pre-send checklist before a bulk campaign and steer clear of the content triggers that raise your spam score.

Or skip the ceremony: preview against your real send path

Everything above is a workflow you assemble by hand: a build step that inlines CSS and generates plaintext, a seed list across four providers, a script to fire the real message at them, and a manual pass through dark mode and images-off on real devices. It is the correct workflow, and most of it is mechanical glue that should already exist around your sender.

Tinbox is a self-contained email stack you deploy onto your own Cloudflare account (D1 for storage, R2 for attachments, Queues for sending, all on your infrastructure). Because the preview renders through the same path that sends, what you see is the inlined, plaintext-paired HTML that actually goes out, not a separate browser approximation that drifts from the real message. The sender generates the plaintext alternative and inlines CSS by default, so the most common "looked fine in preview" failure (Gmail dropping your <style> block) cannot happen. Seed sends go through your own authenticated domain, so you verify rendering and authentication in one shot, and because there is no per-recipient pricing, sending to a seed list before every campaign costs nothing extra.

One honest caveat: no stack can render an exotic client it cannot see, and Tinbox does not bundle a 90-client screenshot farm. It gets the mechanical parts right (real-path preview, inlined CSS, plaintext generation, table-safe templates) so your manual QA is only about judgment. For broad legacy-client coverage on a big campaign, a screenshot service still earns its keep alongside it. The full deliverability picture, of which rendering is one part, lives in the email deliverability guide.

Deploy this yourself: npx tinbox init example.com. The deploy wires up authenticated sending with CSS inlining and plaintext generation from the first message, so the preview you test matches what your recipients receive. See the docs for template authoring and campaign previews.

Frequently asked questions

How do I test how my email renders across clients before sending it?

Render your template to final HTML, then look at it in the clients that actually break things: Outlook on Windows (which uses Word's rendering engine, not a browser), Gmail web and the Gmail app, Apple Mail on macOS and iOS, and one Android client. The reliable method is a seed send: deliver the real message to test accounts you own at each provider and inspect them on real devices. Screenshot services like Litmus and Email on Acid automate this across dozens of clients, but a four-account seed list catches most failures for free. Always check dark mode and images-disabled separately, because both change rendering independently of the client.

Why does my email look broken in Outlook but fine everywhere else?

Because Outlook on Windows renders HTML with Microsoft Word's engine, not a web browser. Word ignores most modern CSS: no flexbox, no grid, unreliable padding and margin on divs, no background images without VML, and no support for many CSS properties you rely on elsewhere. Layouts that use semantic HTML and CSS positioning collapse. The fix is to build email layouts with nested HTML tables and inline styles, use VML for background images, and add conditional comments targeting Outlook (mso) for spacing. Frameworks like MJML and React Email generate this table-based HTML for you so you do not hand-write it.

How do I test email dark mode rendering?

Toggle dark mode on each test device and re-inspect, because clients handle it differently and there is no single standard. Apple Mail and some others may auto-invert your colors, turning a dark logo on a white background into an invisible dark-on-dark mess. Gmail's app partially inverts. Defend against it by using transparent PNGs with a stroke or padding so logos survive inversion, setting explicit background colors on table cells, adding the color-scheme and supported-color-schemes meta tags, and testing the actual message in dark mode rather than trusting a light-mode preview.

Are Litmus and Email on Acid worth it, or can I test for free?

For free, send seed messages to your own accounts at Gmail, Outlook.com, Yahoo, and Apple iCloud and inspect them on a phone and a desktop. That catches the majority of real failures at zero cost and is enough for most transactional mail. Paid services (Litmus, Email on Acid) render screenshots across 40-90 clients including legacy Outlook versions and many mobile apps, plus spam-filter and link checks. They are worth the subscription if you send high-volume marketing campaigns where a rendering bug reaches hundreds of thousands of people, and overkill for a low-volume app that sends receipts.

What should I check before sending any email?

Run a fixed pre-send checklist: the message has both a plaintext and HTML part, CSS is inlined, the layout uses tables (not flexbox or grid) so Outlook survives, every image has alt text and the email reads with images blocked, dark mode does not invert your logo into oblivion, links work and point to your own domain, the subject and preheader read correctly truncated on mobile, and the message renders sanely at a 320px mobile width. Verify on at least Gmail, Outlook, Apple Mail, and one mobile client before a real send.