BuilderJS

Table-Based Email Layout: Why It Persists in 2026 and How to Nest Tables Correctly

Build a marketing email with the layout tools you'd use for a website — flexbox, CSS grid, a few div columns — and it will look fine in your browser and then fall apart in Outlook. That's not a bug in your CSS; it's the inbox. Desktop Outlook on Windows still renders mail through Microsoft's Word engine, which ignores flexbox and grid, so the dependable way to control structure across email clients in 2026 is the same one developers have leaned on for two decades: nested HTML tables. Not because tables are elegant, but because they are the lowest common denominator every mail client understands. The catch is that "use tables" is only the headline. The reliable result lives in the details — `role="presentation"` so screen readers don't announce a layout grid as data, defining width in two places so Outlook and modern clients agree, and resetting `cellpadding`/`cellspacing`/`border-collapse` so cells don't inherit phantom gaps. This guide explains why tables persist, how to nest them without breaking, the small rules that actually matter, and how an editor can emit the whole bulletproof structure so you never hand-write a `<td>` again.

Mount it

// Design on a modern canvas; export bulletproof nested-table email HTML.
  // BuilderJS is a DOM-mount bundle — no npm package, no React component.
  const builder = new Builder({
    mainContainer: '#bjs-main',
    widgetsContainer: '#bjs-widgets',
    settingsContainer: '#bjs-settings',
    saveUrl: '/api/templates',   // your own endpoint — BuilderJS never persists
    loadAssets: 'auto',
  });
  
  // When the user is done, get the table-based email HTML + lossless JSON state.
  async function exportEmail() {
    const emailHtml = builder.getHtml(); // flex/grid → nested role=presentation tables,
                                         // define-width-twice, cellpadding/cellspacing
                                         // reset, MSO ghost tables, @media stacking
    const designJson = builder.getData(); // persist this as your source of truth
  
    // BuilderJS builds the markup — YOU own the send.
    await fetch('/api/save-template', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ html: emailHtml, data: designJson }),
    });
    // ...then hand emailHtml to your ESP / SMTP service to actually deliver it.
  }

Why email still uses tables in 2026

Email clients are not browsers. They run untrusted HTML from strangers, so they sanitize hard and render with engines that have nothing to do with Chrome or Safari. The headline case is Microsoft: desktop Outlook on Windows renders mail through the Word HTML engine, which has no support for flexbox or CSS grid and unreliable support for modern positioning, so any layout built on those will collapse there (as of June 2026; Microsoft has signaled a move away from the Word-based desktop Outlook around late 2026, but the installed base remains — verify on Microsoft's own pages). Tables are the fallback that works everywhere because every mail client, old and new, understands a `<table>` with rows and cells. That's the whole reason the technique refuses to die: it is the one layout primitive with near-universal support across the fragmented client landscape — Gmail, Outlook, Apple Mail, Yahoo, and the long tail of mobile and webmail apps. So in 2026 a structurally sound email is still a stack of tables, and the modern-CSS niceties (media queries, dark mode) are progressive enhancement layered on top — never the load-bearing structure.

The wrapper and container structure

A robust email isn't one table; it's a small set of nested ones with distinct jobs. The outermost is the full-width wrapper — a table at `width:100%` whose only purpose is to give you a paint surface that fills the client's viewport and lets you center the content. Inside it sits the container (or body) table, constrained to a fixed content width — commonly 600px, which fits comfortably in most preview panes. Inside the container go your content rows, and any multi-column section becomes its own nested table whose `<td>` cells are the columns. The pattern is wrapper → container → rows → column tables, nesting deeper only where the design needs columns. Keep nesting shallow and deliberate: every extra level is more markup to bloat the file and more chances for a client to mis-render. The mental model is plumbing, not painting — outer table positions and centers, inner tables hold the actual blocks, and columns are cells, not floated divs.

The four rules that make nesting bulletproof

Four details separate a table layout that holds together from one that leaks. First, role=presentation: add `role="presentation"` to every layout table so assistive technology treats it as visual scaffolding, not a data grid it should read row-by-row — without it, screen readers announce 'table, 3 columns' over your hero image. Second, define width twice: set both the HTML attribute (`width="600"`) and the inline CSS (`style="width:600px"`) on fixed-width tables and cells, because Outlook's Word engine trusts the attribute while modern clients honor the CSS — give them each what they read and they agree. Third, reset cellpadding/cellspacing: put `cellpadding="0" cellspacing="0"` on the tag and `border-collapse:collapse` in the style, or clients add their own default gaps between cells and you fight invisible spacing forever; control padding deliberately on each `<td>` instead. Fourth, suppress Outlook's phantom spacing with `mso-table-lspace:0pt; mso-table-rspace:0pt` and `table-layout:fixed` so columns don't drift. Get these four right and the same markup renders consistently from Outlook 2016 to Apple Mail.

Design in a normal canvas — the builder emits the tables

Hand-writing nested tables with all four rules, on every section, for every email, is exactly the tedious, error-prone work you want a tool to own. This is where BuilderJS fits. It is a drag-and-drop builder library you embed inside your own app, where your users design on an ordinary modern canvas — flexbox-style rows, columns, drag-and-drop blocks — without thinking about tables at all. When you export email, its EmailExportPipeline does the translation: it converts each flex/grid layout into a real nested `<table>`, stamps `role="presentation"`, sets `cellpadding="0" cellspacing="0"` with `border-collapse:collapse; table-layout:fixed`, writes the container width both as an attribute and inline (the define-width-twice rule), adds `mso-table-lspace`/`mso-table-rspace` resets, wraps the body in MSO conditional 'ghost table' comments for the Outlook Word renderer, injects an `@media (max-width:600px)` rule so columns stack on mobile, and gives every `<img>` `max-width:100%; height:auto` so images stay fluid. Nested grids are processed deepest-first so inner column tables nest correctly inside outer ones. You design in the comfortable canvas; the pipeline emits the bulletproof structure.

It builds the markup — you own the send and the page output

Keep the boundary clear: BuilderJS is the editor, not a sending platform. It produces the email HTML — nested tables, inlined CSS, MSO conditionals and all — and then your own stack takes over. You bring the ESP or SMTP service that actually delivers the message; BuilderJS never sends, hosts a list, schedules a campaign, or handles deliverability. The workflow is design, then `getHtml()` for the rendered table-based markup and `getData()` for the lossless JSON you persist, then hand the HTML to whatever sends your mail. The same editor also has a second output mode: for landing and web pages it emits clean, modern-CSS HTML — no tables, because real browsers handle flexbox and grid fine — so you don't run two editors or hand-rewrite a campaign into web markup. Pick the output that matches the destination: ruthless nested tables for the inbox, normal responsive HTML for the browser. Every email your users design runs through that one EmailExportPipeline, so the same transform proven on the 30+ bundled templates and 200+ samples — Litmus-tested across 22 email clients — is the transform applied to your output too. You can try the editor live at builder.emotsy.com.

Frequently asked questions

Explore