How to Fix Outlook HTML Email Rendering
You designed an email that looks perfect in Gmail and Apple Mail, then opened it in desktop Outlook on Windows and watched it fall apart: columns stacked wrong, padding doubled, background colors vanished, an SVG turned into a broken-image icon. Outlook isn't being difficult on purpose. Desktop Outlook on Windows renders HTML through Microsoft Word's layout engine, not a browser engine, and Word ignores most of modern CSS. Flexbox and grid do nothing. Margins behave oddly. `max-width` is unreliable. Background images need a special syntax. If you build email the way you build web pages, Outlook is where it breaks. This guide explains, in plain terms, why Outlook renders email the way it does, the concrete techniques that actually fix the common failures (tables, inline CSS, MSO conditional comments, ghost tables, VML, and SVG-to-PNG fallbacks), and how a drag-and-drop builder can apply much of it automatically so you don't hand-write a conditional comment for every send. Note up front: BuilderJS is the editor you embed — it builds Outlook-safe markup; it does not send your mail.
Mount it
// BuilderJS is a self-hosted source library you mount in your own app —
// there is no npm package and nothing phones home. Load the shipped bundle:
// <link rel="stylesheet" href="/dist/builder.css">
// <script src="/dist/builder.js"></script>
const builder = new Builder({
mainContainer: document.getElementById('builder-main'),
widgetsContainer: document.getElementById('builder-widgets'),
settingsContainer: document.getElementById('builder-settings'),
saveUrl: '/api/save', // your endpoint; builder.save() POSTs html+data
upload: { url: '/api/upload', maxBytes: 5 * 1024 * 1024 },
loadAssets: 'auto',
});
// Persist the design as JSON, and pull the export when you're ready to send:
const json = builder.getData(); // JSON for round-trip editing
const html = builder.getHtml(); // for email export, Outlook-safe HTML
// React: there is NO published <EmailEditor/> package — wrap the engine
// yourself in a thin self-owned component (mount in an effect, expose via ref).
function EmailBuilder({ onReady }) {
const main = useRef(null), widgets = useRef(null), settings = useRef(null);
useEffect(() => {
const instance = new Builder({
mainContainer: main.current,
widgetsContainer: widgets.current,
settingsContainer: settings.current,
saveUrl: '/api/save',
});
onReady?.(instance); // parent calls instance.getHtml() later
}, []);
return (
<div className="bjs">
<div ref={widgets} /><div ref={main} /><div ref={settings} />
</div>
);
}
Why Outlook renders email differently
The single most useful fact about Outlook email rendering is that desktop Outlook on Windows (2007 through current perpetual builds) uses Microsoft Word's rendering engine, not a web browser engine. Word was built to lay out documents, not web pages, so a large slice of CSS that every browser supports is simply ignored. Flexbox and CSS grid produce nothing — multi-column layouts collapse. `float` and `position` are unreliable. `max-width` on images and tables is frequently ignored, which is why widths blow out. Padding on `<div>` elements is inconsistent, and `margin` often behaves unexpectedly. Background images via the CSS `background` property don't render at all without a Microsoft-specific workaround. As of June 2026, Microsoft has signaled a move toward a new Outlook that uses a modern (web-based) engine, but the Word-based desktop client remains widely deployed and must still be supported — verify the current state on Microsoft's own pages. The practical takeaway: target the lowest common denominator. An email that renders in Word-engine Outlook renders nearly everywhere; the reverse is not true.
Tables and inline CSS: the non-negotiable base
Two techniques carry most of the weight. First, lay out with HTML tables, not `<div>` and flexbox. Word's engine understands tables reliably, so every multi-column row, every spacer, and the overall page frame should be a `<table role="presentation" cellpadding="0" cellspacing="0" border="0">`. The `role="presentation"` keeps screen readers from announcing layout tables as data. Set explicit pixel `width` on cells rather than relying on `max-width`. Second, inline your CSS. Gmail and other clients strip `<head>` `<style>` blocks under several conditions, and Outlook is happiest with styles applied directly to elements via the `style` attribute. So `<td style="font-size:16px;color:#333;padding:12px;">` beats a shared class every time. You still keep a small head `<style>` for things that can only live there — `@media` queries for mobile stacking, `:hover`, `@font-face` — but treat inline styles as the resilient base layer. Add `mso-table-lspace:0pt;mso-table-rspace:0pt` to tables to kill the phantom horizontal spacing Outlook injects around them, and `border-collapse:collapse;table-layout:fixed` so widths hold.
MSO conditional comments and ghost tables
Some fixes have to target Outlook specifically without touching other clients. That's what Microsoft Office conditional comments do. A block wrapped in `<!--[if mso | IE]> ... <![endif]-->` is read only by Word-engine Outlook (and old IE-based clients) and ignored as a plain HTML comment everywhere else. The classic use is the 'ghost table': because Outlook ignores `max-width`, a centered, fixed-width email body can stretch full-window in Outlook unless you wrap the content in an MSO-only table that enforces the width. The pattern looks like `<!--[if mso | IE]><table role="presentation" width="600" align="center"><tr><td><![endif]-->` before the body and the closing tags after it. Gmail and Apple Mail never see that table; Outlook uses it to constrain the layout to 600px. The same conditional-comment technique is also where teams inject Outlook-only spacing, set `<o:OfficeDocumentSettings>` to pin DPI so images don't scale up, and supply VML markup for background images (Outlook reads `<v:rect>`/`<v:fill>` instead of CSS `background`). These last three are hand-coding patterns you add when a design needs them; they are fiddly and easy to get subtly wrong by hand — which is exactly why generating the parts you can is better than typing them.
Images, SVG, fonts, and dark mode in Outlook
A few more Outlook traps round out the list. SVG: Word-engine Outlook does not render inline SVG at all, so any vector graphic (icons, charts, decorative shapes) must be rasterized to PNG with a real `width`/`height`, or it shows as a broken image. Web fonts: Outlook ignores `@font-face` and falls back to a system font, so always declare a sensible `font-family` fallback stack and don't assume your brand font appears. Images: set explicit `width` and `height` attributes plus `style="display:block"` to avoid gaps, and remember Outlook may upscale images on high-DPI displays unless you pin DPI via the conditional `<o:OfficeDocumentSettings>` block. Dark mode: Outlook's color-scheme handling differs from Gmail and Apple Mail, and `@media (prefers-color-scheme)` support is inconsistent, so test light and dark rather than trusting one. None of this is exotic once you know it — but it is a long checklist to apply correctly on every send, across every edit, which is the real argument for tooling.
How a builder applies all of this for you
This is where an embeddable editor earns its place. BuilderJS is a drag-and-drop builder library you mount inside your own app to design HTML email and web pages; it produces the markup, and you bring your own sending path (your ESP, SMTP, or backend). When you export email, its EmailExportPipeline transforms the editor's modern flexbox-and-`<style>` design into Outlook-safe HTML automatically: it converts flexbox/grid layouts into nested `role="presentation"` tables, wraps the body in an MSO conditional-comment ghost table to enforce width in Outlook, adds `mso-table-lspace:0pt;mso-table-rspace:0pt` and `table-layout:fixed`, swaps its built-in styled social-icon SVGs for pre-rendered PNGs (Outlook strips inline SVG), generates a mobile-stacking `@media` rule with `mso-hide:all` visibility handling for Outlook, and runs a CSS inliner that pushes every safe declaration onto its element. (For your own custom inline SVG or charts, rasterize to PNG before export, since the pipeline only auto-swaps its own marked social icons.) The library's 30+ templates and 200+ samples are Litmus-tested across 22 email clients — including the fussy Outlook desktop builds — so the shipped output is checked, not assumed. You design once in any framework (React, Vue, Svelte, Angular, plain HTML), call `getData()` to persist the JSON, and export inlined, Outlook-safe email HTML when you're ready to hand it to whatever sends your mail. For non-email work, the same design exports clean modern page HTML — no Word-engine workarounds where they aren't needed.
Frequently asked questions
-
Why does my email look fine in Gmail but break in Outlook?
Because they use different rendering engines. Gmail (and Apple Mail) use browser-grade engines that support modern CSS, while desktop Outlook on Windows renders through Microsoft Word's engine, which ignores flexbox, grid, max-width, and CSS background images. A layout that relies on modern CSS works in Gmail and collapses in Outlook. The fix is to use HTML tables for layout, inline your CSS, and add Outlook-specific MSO conditional comments — then test in Outlook specifically, not just Gmail.
-
What are MSO conditional comments and ghost tables?
MSO conditional comments are HTML comments in the form <!--[if mso | IE]> ... <![endif]--> that only Word-engine Outlook reads; every other client treats them as a plain comment and ignores them. A 'ghost table' is a fixed-width table placed inside those comments to constrain the email body to a set width (e.g. 600px) in Outlook, since Outlook ignores max-width. The same comments are also where you put Outlook-only spacing, DPI settings, and VML background-image markup. They're effective but easy to get wrong by hand, so most teams generate the parts they can with tooling rather than writing them manually.
-
Why does my SVG show as a broken image in Outlook?
Word-engine desktop Outlook does not render inline SVG at all, so any vector graphic appears as a broken-image placeholder. The fix is to rasterize SVG to PNG with explicit width and height attributes before sending. BuilderJS's EmailExportPipeline does this automatically for its own built-in styled social icons — it swaps each marked icon SVG for a pre-rendered PNG during email export. For your own custom inline SVG or charts, convert them to PNG before export so they render in Outlook the same as everywhere else.
-
Does BuilderJS send the email or test it in Outlook for me?
No. BuilderJS is the editor you embed in your own app — it builds and exports Outlook-safe HTML, including the table conversion, inline CSS, MSO conditional comments, and the built-in social-icon PNG swap. It does not send email, manage lists, or run inbox tests. Its templates are Litmus-tested across 22 clients during development, but for your own campaigns you connect the exported HTML to your own sending path and run your own client testing. It owns the markup; you own the send and the test.
-
Do I still need inline CSS if I'm targeting Outlook?
Yes. Inline CSS is the resilient base for every major client, Outlook included — styles applied directly to elements via the style attribute survive sanitizing far more reliably than head or external CSS. Keep a small head <style> block only for things that can't be inlined, such as @media queries for mobile stacking and :hover. Outlook ignores most media queries anyway, so the inline styles are what actually hold your Outlook layout together; the head styles are progressive enhancement on top.