Inbox previews

Deliverability

Inbox previews

See how a design renders in real email clients before you send. In Preview, a client switcher simulates each client's rendering quirks, so you catch breakage (Outlook's square corners, Gmail-mobile stripping <style>) without leaving the editor.

Inbox previews are a paid feature, the switcher appears only when the plan grants preview.inbox (see Plans & entitlements). The switcher is a client-side simulation of documented quirks. For real screenshots, connect your own preview service (below).

Simulated clients#

PropTypeDefaultDescription
Apple MailWebKit-Near-full CSS, the reference render.
Gmail (Web)Blink-Keeps <style> but drops @media queries; clips past ~102KB.
Gmail (Mobile)Blink-Strips <style> entirely, inline CSS only, no responsive.
Outlook (Windows)Word-No border-radius, box-shadow, background-image, or media queries; fixed widths.
Outlook.comBlink-Better than desktop; strips some <style> rules.
Yahoo MailBlink-Good support; rewrites some class/id names.

Real inbox previews#

The switcher above is a simulation. For pictures of the email in the real mail apps, connect a service such as Email on Acid or Litmus. Mailkaro does not run a render farm and does not resell one: you use your own account with the service, and the pictures come from it. With a service connected, Preview gets a Real inbox previews button. Pick the mail apps, run the test, and the pictures arrive one by one (they take from a few seconds to a couple of minutes). Click one to see it larger.

The service's key never reaches the browser. It stays in the code on your server that talks to the service, and the editor only calls your route. With the backend SDK it is three steps:

tsserver.ts
import { Mailkaro } from "email-craft-sdk";

// EMAILONACID_API_KEY and EMAILONACID_PASSWORD in the environment turn on Email on Acid
const admin = Mailkaro.fromEnv();

// 1. the editor's runtime route already tells the editor the capability is on (capabilities.inboxPreviews)
// 2. one route for the editor to call, next to your /mailkaro/ai route
app.post("/mailkaro/inbox-previews", async (req, res) => {
  const { status, body } = await admin.inboxPreviews(req.body);
  res.status(status).json(body);
});

Another service? Give the SDK a provider with three functions, and the rest is the same. In a page that mounts the editor directly, pass the provider as config.inboxPreviews (or the inboxPreviews prop) instead of using a route:

tsprovider.ts
const admin = new Mailkaro({
  serverKey: process.env.MAILKARO_SERVER_KEY!,
  inboxPreviews: {
    name: "Litmus",                               // shown to the person
    clients: async () => [{ id: "gmail", label: "Gmail", group: "Webmail" }],
    start: async ({ html, subject, clients }) => ({ id: await startTest(html, subject, clients) }),
    check: async (id) => ({
      done: false,
      shots: [{ client_id: "gmail", status: "ready", image_url: "https://..." }]   // status: pending | ready | failed
    })
  }
});
Running a test sends the email's HTML to the service, and the editor says so. A picture is only shown when its address is https (or an embedded PNG, JPEG, WebP or GIF), so a service cannot put script on the page. Email on Acid's adapter is written from its public API documentation and tested against a stand-in of that API, not yet against a live account, because a paid account is needed. Check the first real run, and tell us if anything differs. Real previews need the plan's inbox previews feature and the Email builder.

Programmatic#

The same transform is exported from the renderer:

tspreview.ts
import { renderDesign, simulateClientHtml, emailClients } from "email-craft-sdk";

const html = renderDesign(design, { fullDocument: true });
const outlookHtml = simulateClientHtml(html, "outlook");   // square corners, no <style>
// emailClients: [{ id, label, engine, width, mobile, notes }]