BuilderJS

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

Explore