Email preview, evolved — by Folderly

See how it renders. Know if it'll land.

Every other preview tool shows you a picture. Paste your email HTML into Frame and get all three: how it actually renders across Gmail, Outlook & Apple Mail, a plain-English list of every line that'll break per client — and the one thing no render tool answers, whether it'll even reach the inbox. Built by the deliverability people.

Honest fidelity — every preview is labeled by how it was produced. No fake screenshots. No signup to try.
V
Built by Vlad Podolyako — founder of Folderly (B2B email deliverability) and Belkins. Frame is the render-QA tool that finally answers the deliverability question too. Pre-launch — the preview below is real and runs entirely in your browser.
Live · runs in your browser

Paste an email. See all three answers.

Drop in your HTML (or load a sample). Frame renders it across client engines, flags what breaks where, and scores deliverability — instantly, with nothing uploaded.

FRAME PREVIEW ENGINE · v0.1 ● Your HTML never leaves this browser — nothing is uploaded or stored
▦
Paste your HTML and hit Preview & analyze
or load a sample on the left — try "Breaks in Outlook"
Fidelity: engine-accurate engine-approximated rule-simulated real-client (Pro)
caniemail-linter — per-client compatibility
–
awaiting
This is the teaser. The full Folderly deliverability score adds your domain's SPF / DKIM / DMARC, blocklist status, and inbox-placement history against real seed accounts. Get the full score →
Why Frame

Three answers. One paste.

2-in-1
Render and deliverability
Every render tool ignores the inbox; every deliverability tool ignores the render. Frame is the only one that does both for arbitrary pasted HTML — because it's built by Folderly.
Honest
Labeled fidelity, never faked
We render the clients we can render honestly and tell you exactly how — engine-accurate, approximated, or rule-simulated. We never show you a screenshot we can't stand behind.
$0→$24
Priced for humans
Litmus now starts at $500/mo and Email on Acid is being folded into a suite. Frame starts free, with a real plan at $24 — for the indie, the agency, the founder they left behind.
The gap nobody fills

Render tools and deliverability tools never talk.

You currently need two tools and still don't get a straight answer. Frame is the bridge.

Render-QA tools LITMUS · EOA

  • Show how it looks
  • Lots of client screenshots
  • Silent on deliverability
  • $99–$500+/mo

Folderly Frame BOTH

  • How it renders, honestly labeled
  • Per-client compatibility linter
  • Will-it-land deliverability score
  • Free → $24

Deliverability tools GLOCKAPPS

  • Inbox placement / spam score
  • Blind to how it renders
  • Separate workflow, separate bill
  • Per-test or $59+/mo
How it works

Paste. Preview. Ship with confidence.

◷ STEP 01
Paste or pull your HTML
Drop raw HTML, or (soon) pull straight from your ESP — Klaviyo, Mailchimp, HubSpot. Nothing is stored.
◷ STEP 02
See render + breakage + score
Engine-accurate previews across the clients that matter, a plain-English per-client linter, and the deliverability verdict — in seconds.
◷ STEP 03
Fix it before you send
Copy-paste fixes for what breaks, re-check, and hit send knowing it looks right and will land. Wire it into CI to check every commit.
Coming from another tool?

We built this for you.

LITMUS RAISED ITS FLOOR TO $500/MO
Priced out of Litmus?
When Validity bought Litmus it killed the affordable plan. If you're an indie, a small team, or a 5–20-client agency, Frame is the home you got evicted from — bring your last campaign and pick up where you left off.
Switch from Litmus →
EMAIL ON ACID → MAILGUN INSPECT · JUNE 2026
Being migrated off Email on Acid?
If you're being moved into a suite you didn't choose, you have an exit. Frame gives you the previews and the linter you relied on — plus the deliverability answer Email on Acid never had.
See the escape hatch →
Pricing

Start free. Pay like a human.

Indicative launch pricing — locking it as part of the early-access round. Email previews metered per (email × client); the linter and deliverability score don't count against your meter.

Free
$0
For the occasional send.
  • 50 previews / mo
  • Render across core clients
  • Compatibility linter
  • Deliverability teaser
Start free
Lite Most popular
$24/mo
For the marketer who ships weekly.
  • 1,000 previews / mo
  • Full deliverability score
  • Source inspector + fix hints
  • All clients + mobile
Reserve Lite
Pro
$69/mo
For teams and power senders.
  • 2,500 previews / mo
  • AI fix copilot
  • API + GitHub Action
  • Team seats
Reserve Pro
Agency
$149/mo
For agencies running 5–20 clients.
  • Pooled previews
  • Multi-client workspaces
  • White-label reports
  • Folderly bundle
Reserve Agency
Active Folderly customers get Frame included · 14-day refund on paid plans
FAQ

The honest answers.

Is this pixel-perfect like Litmus?

No — and we'll never pretend it is. Litmus and Email on Acid run real-client screenshot farms across 90+ clients. Frame renders the major clients through their real engines (WebKit for Apple/iOS, Chromium for Gmail/Outlook.com after their CSS rules) and rule-simulates Outlook desktop. Every preview is labeled with exactly how it was made. That catches the ~80–85% of breakage that actually matters, in seconds — and we tell you honestly where the line is.

What makes the deliverability part different?

It's the whole point. No render-QA tool tells you whether your email will land in the inbox. Frame does, because it's built by Folderly — a deliverability company. The demo above shows a content-based teaser; the full score adds your domain's authentication, blocklist status, and real inbox-placement signals.

Is my email HTML stored or used to train AI?

No. The preview above runs entirely in your browser — nothing is uploaded. In the product, HTML is processed ephemerally for the render and discarded; we never retain it without explicit opt-in and we never train AI on your content.

When does it launch and what's the price?

It's in early access now. Pricing shown is indicative and being locked with the first cohort — join the list and you'll help set it (and get early-bird terms). Active Folderly customers get Frame included.

I'm coming from Litmus / Email on Acid. Can I bring my stuff?

Yes — your assets are just HTML, so there's nothing proprietary to export. Paste your last campaign and you're previewing in seconds. Tell us what you relied on and we'll make sure it's covered.

Stop sending half-blind.

Join the early list. You'll get first access, early-bird pricing, and a say in what we build — starting with the clients and ESPs you care about most.

By Folderly · render preview + deliverability · nothing uploaded · vladislav@belkins.io
', newsletter: '\n' + '\n' + '
\n' + '

This week in email

\n' + '

Three things worth your attention this week, plus a deep dive on rendering.

\n' + '
\n' + '

1. The Outlook problem

\n' + '

Why your beautiful email looks broken in Outlook.

\n' + 'Read the full issue
', broken: '\n' + '\n' + '
\n' + '
Launch day is here 🚀
\n' + '
\n' + '
\n' + '

Feature one

A grid layout that Outlook will not respect.

\n' + '
\n' + '

Feature two

Background images get dropped in Outlook desktop.

\n' + '
\n' + 'GET STARTED NOW!!!
\n' + '' }; // ---- Clients & their honest fidelity + simulation overrides ---- var CLIENTS = [ { id: "apple", name: "Apple Mail / iOS", dot: "#1d1d1f", fid: "acc", fidLabel: "engine-accurate", note: "WebKit — renders modern CSS faithfully.", sim: "" }, { id: "gmail", name: "Gmail", dot: "#EA4335", fid: "apx", fidLabel: "engine-approximated", note: "Chromium after Gmail's CSS rules. position neutralized; clips past ~102KB.", sim: '[style*="position:absolute"],[style*="position: absolute"],[style*="position:fixed"],[style*="position: fixed"]{position:static !important;}' }, { id: "outlook", name: "Outlook (desktop)", dot: "#0A66C2", fid: "sim", fidLabel: "rule-simulated", note: "Word engine rules: no fl/grid, no positioning, no rounded corners, no CSS background-images, system fonts only.", sim: '*{font-family:Arial,Helvetica,sans-serif !important;}' + '[style*="display:flex"],[style*="display: flex"],[style*="display:grid"],[style*="display: grid"]{display:block !important;}' + '[style*="position:absolute"],[style*="position: absolute"],[style*="position:fixed"],[style*="position: fixed"]{position:static !important;}' + '[style*="border-radius"]{border-radius:0 !important;}' + '[style*="box-shadow"]{box-shadow:none !important;}' + '[style*="linear-gradient"]{background:#cccccc !important;}' } ]; // ---- caniemail-style rules: detect feature, name clients affected ---- var LINT_RULES = [ { re: /style\s*=\s*["'][^"']*position\s*:\s*(absolute|fixed)/i, feat: "position: absolute/fixed", clients: "Outlook, Gmail", sev: "bad", msg: "Outlook ignores positioning entirely; Gmail strips it. Layout will collapse — use tables/padding." }, { re: /style\s*=\s*["'][^"']*display\s*:\s*(flex|grid)/i, feat: "display: flex / grid", clients: "Outlook", sev: "bad", msg: "Outlook desktop (Word engine) ignores flexbox/grid. Columns stack. Use nested tables for multi-column." }, { re: /linear-gradient|radial-gradient/i, feat: "CSS gradient", clients: "Outlook", sev: "warn", msg: "Outlook drops CSS gradients to a flat/no background. Add a solid background-color fallback." }, { re: /border-radius/i, feat: "border-radius", clients: "Outlook", sev: "warn", msg: "Outlook desktop renders square corners. Fine to use, just expect no rounding there." }, { re: /box-shadow/i, feat: "box-shadow", clients: "Outlook", sev: "warn", msg: "box-shadow is dropped in Outlook desktop. Use a bordered table for separation instead." }, { re: /]*>[\s\S]*?@media/i, feat: "@media queries", clients: "Gmail (app), Outlook", sev: "warn", msg: "Media queries are unreliable in Gmail's app and several Outlooks. Design mobile-friendly without depending on them." }, { re: /]*\balt\s*=)[^>]*>/i, feat: "image missing alt", clients: "All + accessibility + spam", sev: "warn", msg: "An has no alt text. Hurts accessibility, looks broken when images are blocked, and nudges spam filters." }, { re: /background-image\s*:|background\s*:\s*url\(/i, feat: "CSS background-image", clients: "Outlook", sev: "warn", msg: "CSS background-images are dropped in Outlook desktop. Use VML or a bulletproof background for hero images." }, { re: /font-family\s*:\s*['"]?(?!arial|helvetica|georgia|times|verdana|tahoma|courier|trebuchet|sans-serif|serif|monospace)/i, feat: "non-system / web font", clients: "Outlook, others", sev: "warn", msg: "Custom web fonts fall back to a system font in Outlook and others. Set a sensible fallback in the stack." }, { re: / and inline are unsupported in most clients. Use a static image with a play overlay instead." } ]; function lint(html) { var found = []; LINT_RULES.forEach(function (r) { if (r.re.test(html)) found.push(r); }); return found; } // ---- deliverability / spam teaser (real client-side heuristics) ---- function analyzeDeliverability(html) { var signals = [], score = 100; var text = html.replace(//gi, " ").replace(/<[^>]+>/g, " ").replace(/&[a-z#0-9]+;/gi, " "); text = text.replace(/\s+/g, " ").trim(); var words = text ? text.split(" ").length : 0; var imgs = (html.match(/ 0 && words < 25 * imgs) { score -= 22; signals.push(["bad", "Image-heavy, text-light", "Roughly " + imgs + " image(s) vs ~" + words + " words. Image-only emails are a classic spam signal — add real text."]); } else if (imgs > 0) { signals.push(["ok", "Healthy image-to-text balance", words + " words of real text alongside " + imgs + " image(s)."]); } else { signals.push(["ok", "Text-based email", "No images — naturally text-forward, which filters like."]); } // spam trigger words var spamWords = ["free", "act now", "limited time", "click here", "guarantee", "winner", "cash", "100%", "risk-free", "buy now", "order now", "urgent", "congratulations"]; var lc = text.toLowerCase(); var hits = spamWords.filter(function (w) { return lc.indexOf(w) !== -1; }); if (hits.length >= 3) { score -= 18; signals.push(["bad", hits.length + " spam-trigger phrases", "Found: " + hits.slice(0, 4).join(", ") + ". Filters weight these heavily — rephrase the salesy ones."]); } else if (hits.length >= 1) { score -= 8; signals.push(["warn", "Some spam-trigger phrases", "Found: " + hits.join(", ") + ". A couple is fine; watch the count."]); } else { signals.push(["ok", "No obvious spam-trigger phrases", "Copy reads clean to a content filter."]); } // ALL CAPS / excessive punctuation if (/[A-Z]{6,}/.test(text) || /!{2,}/.test(text)) { score -= 12; signals.push(["warn", "Shouting detected", "ALL-CAPS runs or multiple '!!' read as spammy. Tone it down, especially near the CTA."]); } else { signals.push(["ok", "No shouting / punctuation spam", "Capitalization and punctuation look natural."]); } // missing alt var missingAlt = (html.match(/]*\balt\s*=)[^>]*>/gi) || []).length; if (missingAlt > 0) { score -= 9; signals.push(["warn", missingAlt + " image(s) missing alt text", "Blocked images show nothing and filters notice. Add descriptive alt text."]); } else if (imgs > 0) { signals.push(["ok", "All images have alt text", "Good for accessibility and for image-blocking clients."]); } // link sanity if (links === 0 && words > 0) { signals.push(["warn", "No links found", "No clear call-to-action link detected. Most marketing emails need at least one."]); } else if (links > 0) { signals.push(["ok", links + " link(s) present", "A clear path to click is present."]); } // unsubscribe if (!/unsubscribe|opt.?out/i.test(html) && words > 0) { score -= 10; signals.push(["warn", "No unsubscribe link detected", "Required for compliant bulk mail (CAN-SPAM / GDPR) and a strong sender-reputation signal."]); } else if (words > 0) { signals.push(["ok", "Unsubscribe present", "Compliance and reputation box checked."]); } score = Math.max(12, Math.min(99, Math.round(score))); return { score: score, signals: signals }; } // ---- render ---- function buildSrcdoc(html, sim) { var inject = sim ? "" : ""; if (/<\/head>/i.test(html)) return html.replace(/<\/head>/i, inject + ""); if (/]*>/i.test(html)) return html.replace(/(]*>)/i, "$1" + inject); return inject + html; } function renderClients(html) { var grid = $("client-grid"); grid.innerHTML = ""; CLIENTS.forEach(function (c) { var card = document.createElement("div"); card.className = "client-card"; var iframe = document.createElement("iframe"); iframe.setAttribute("sandbox", "allow-same-origin"); iframe.setAttribute("scrolling", "no"); iframe.setAttribute("title", c.name + " preview"); iframe.setAttribute("loading", "lazy"); iframe.srcdoc = buildSrcdoc(html, c.sim); card.innerHTML = '
' + esc(c.name) + '' + '' + esc(c.fidLabel) + '
'; var frameWrap = document.createElement("div"); frameWrap.className = "client-frame"; frameWrap.appendChild(iframe); card.appendChild(frameWrap); var foot = document.createElement("div"); foot.className = "client-foot"; foot.innerHTML = c.note; card.appendChild(foot); grid.appendChild(card); }); } function typeLog(logEl, lines) { logEl.innerHTML = ""; var i = 0; (function next() { if (i >= lines.length) return; var ln = document.createElement("div"); ln.className = "ln " + (lines[i].cls || ""); ln.innerHTML = lines[i].t; logEl.appendChild(ln); logEl.scrollTop = logEl.scrollHeight; i++; setTimeout(next, 90); })(); } function renderLint(html) { var found = lint(html); var cnt = $("lint-cnt"); cnt.textContent = found.length; cnt.className = "cnt" + (found.some(function (f) { return f.sev === "bad"; }) ? " bad" : found.length ? " warn" : ""); var lines = [{ t: "$ frame lint email.html # caniemail ruleset", cls: "dim" }]; if (!found.length) { lines.push({ t: "✓ No client-compatibility issues found across the major clients.", cls: "ok" }); lines.push({ t: " Renders cleanly in Apple Mail, Gmail and Outlook by our ruleset.", cls: "dim" }); } else { found.forEach(function (f) { var glyph = f.sev === "bad" ? "✗" : "▲"; lines.push({ t: glyph + " " + esc(f.feat) + " → " + esc(f.clients) + "", cls: f.sev }); lines.push({ t: " " + esc(f.msg), cls: "dim" }); }); var bad = found.filter(function (f) { return f.sev === "bad"; }).length; lines.push({ t: "— " + found.length + " issue(s): " + bad + " breaking, " + (found.length - bad) + " warning(s).", cls: bad ? "bad" : "warn" }); } typeLog($("scan-log"), lines); } function renderDeliv(html) { var r = analyzeDeliverability(html); var col = r.score >= 80 ? "var(--ok)" : r.score >= 60 ? "var(--warn)" : "var(--bad)"; var verdict = r.score >= 80 ? "Likely to land" : r.score >= 60 ? "At some risk" : "Likely filtered"; $("dv-score").innerHTML = r.score + '/100'; $("dv-score").style.color = col; var v = $("dv-verdict"); v.textContent = verdict; v.style.color = col; var ring = $("dv-ring"); ring.style.width = r.score + "%"; ring.style.background = col; var dc = $("deliv-cnt"); dc.textContent = r.score; dc.className = "cnt" + (r.score >= 80 ? "" : r.score >= 60 ? " warn" : " bad"); var html2 = ""; r.signals.forEach(function (s) { var glyph = s[0] === "ok" ? "✓" : s[0] === "warn" ? "!" : "✕"; html2 += '
' + glyph + '' + esc(s[1]) + '' + esc(s[2]) + '
'; }); $("dv-signals").innerHTML = html2; } function run() { var html = $("html-in").value.trim(); if (!html) { $("html-in").focus(); return; } $("demo-empty").style.display = "none"; $("tabs").style.display = "flex"; activate("preview"); renderClients(html); renderLint(html); renderDeliv(html); try { window.va && window.va("event", { name: "tool_completed", tool: "frame_preview" }); } catch (e) {} } function activate(pane) { document.querySelectorAll(".tab").forEach(function (t) { t.classList.toggle("active", t.getAttribute("data-pane") === pane); }); document.querySelectorAll(".tabpane").forEach(function (p) { p.classList.toggle("active", p.id === "pane-" + pane); }); } document.querySelectorAll(".tab").forEach(function (t) { t.addEventListener("click", function () { activate(t.getAttribute("data-pane")); }); }); document.querySelectorAll(".sample-btn").forEach(function (b) { b.addEventListener("click", function () { var s = SAMPLES[b.getAttribute("data-sample")]; if (s) { $("html-in").value = s; run(); document.getElementById("demo").scrollIntoView({ behavior: "smooth", block: "start" }); } }); }); $("run-btn").addEventListener("click", run); })();