Multi-column layouts in email
Why flexbox and grid aren't safe in email, and the table pattern that works everywhere
The problem
You want two columns side by side. On the web you'd reach for display: flex or display: grid. In email, both are unreliable. The root cause is Gmail.
Gmail strips <style> blocks and <head> content from incoming email, so any CSS that isn't written directly on the element as a style="" attribute can simply vanish.
But inlining your styles doesn't save you either. Even style="display: flex" directly on the element is unreliable in Gmail. Flexbox depends on parent-child relationship properties like grow, align-items, and justify-content, and Gmail's rendering engine doesn't consistently honor those.
Making it worse: "Gmail" is not one renderer. The web app, the native iOS app, the native Android app, and Gmail rendering non-Gmail accounts (sometimes called GANGA) each preprocess HTML differently. A layout that works in one Gmail context silently collapses in another.
Why it's hard
The only layout primitive that renders predictably everywhere (Gmail web, Gmail mobile, Apple Mail, Outlook, Yahoo) is <table>. HTML tables predate every modern email client, and every renderer handles them correctly. Production HTML email still looks like it's from 2005 under the hood for exactly this reason.
Table layout has its own complexity though. Column widths must be set in absolute pixels; there's no flex: 1 shorthand. Mobile stacking requires a media query that overrides those pixel widths, but Gmail strips <style> blocks, so the media query never fires there.
Classic Outlook (the Windows desktop app) also doesn't support media queries and needs pixel widths set via the HTML width attribute, not just CSS. You're writing two rendering paths in one template.
The manual solution
A two-column layout at 600px total width, with each column at 300px, that stacks to a single column on mobile:
<!-- Outer wrapper table: centers content at 600px -->
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0"
style="max-width:600px; width:100%;">
<tr>
<!-- Left column: 50% width -->
<!--[if mso]>
<td width="300" valign="top">
<![endif]-->
<!--[if !mso]><!-->
<td class="col" valign="top"
style="display:inline-block; width:100%; max-width:300px; vertical-align:top;">
<!--<![endif]-->
<div style="padding:16px;">
<p style="margin:0; font-family:sans-serif;">Left column content</p>
</div>
</td>
<!-- Right column: 50% width -->
<!--[if mso]>
<td width="300" valign="top">
<![endif]-->
<!--[if !mso]><!-->
<td class="col" valign="top"
style="display:inline-block; width:100%; max-width:300px; vertical-align:top;">
<!--<![endif]-->
<div style="padding:16px;">
<p style="margin:0; font-family:sans-serif;">Right column content</p>
</div>
</td>
</tr>
</table>
</td>
</tr>
</table>The display: inline-block on each <td> is what lets the columns collapse to a stacked, single-column layout on mobile later (a plain table-cell can't be forced to wrap onto a new line); at full width they're side by side either way. The <!--[if mso]> conditional comments serve a separate path to classic Outlook, which ignores display: inline-block and needs a plain <td> with a hard pixel width attribute instead.
For clients that support <style> blocks (Apple Mail, Outlook.com, most mobile clients except Gmail), add a media query to stack columns on small screens:
<style>
@media only screen and (max-width: 480px) {
/* Stack columns vertically on mobile */
.col {
display: block !important;
width: 100% !important;
max-width: 100% !important;
}
}
</style>The !important is required. Inline styles have higher specificity than a class rule. Without it, the inlined max-width: 300px wins and the columns never stack, even in clients that fully support media queries. Note that this media query never fires in Gmail at all, since Gmail strips <style> blocks. Gmail mobile users will always see the fixed-width columns.
Gotchas to watch for
- Gmail mobile won't stack. Gmail strips
<style>blocks, so the mobile media query never fires there. Columns stay side by side regardless of screen width on all Gmail apps. Design columns to be legible at their fixed pixel widths on a small screen, or go single-column if your audience is predominantly Gmail mobile. - Gaps between columns.
gapandcolumn-gapdon't apply to table-based columns - they're flexbox/grid-only properties, and even in the clients that support flex, coverage is inconsistent. Usepaddingon the inner div of each column, or add a fixed-width spacer<td>between them and subtract its width from your total. - Three columns is harder. Three equal columns at 600px with inner padding need exact pixel math; there's no tolerance for rounding errors. Any whitespace between
<td>tags in the source can cause the third column to wrap. Keep the HTML tight and verify in a real email client, not just a browser. - Content height mismatches. When one column has more content than another, the shorter one won't fill the vertical space.
align-items: stretchdoesn't exist here. Apply background colors on the<td>itself rather than the inner div: cells in a table row stretch to match the tallest cell automatically, so a cell background fills the full column height even when content length differs, while a div only wraps its own content. Also design content so column lengths are roughly similar.
For three columns, here's the full pattern:
<!-- Three equal columns at 200px each (600px total) -->
<table role="presentation" width="600" cellpadding="0" cellspacing="0">
<tr>
<!--[if mso]><td width="200" valign="top"><![endif]-->
<!--[if !mso]><!-->
<td class="col" style="display:inline-block; width:100%; max-width:200px; vertical-align:top;">
<!--<![endif]-->
<div style="padding:16px;">Column 1</div>
</td>
<!--[if mso]><td width="200" valign="top"><![endif]-->
<!--[if !mso]><!-->
<td class="col" style="display:inline-block; width:100%; max-width:200px; vertical-align:top;">
<!--<![endif]-->
<div style="padding:16px;">Column 2</div>
</td>
<!--[if mso]><td width="200" valign="top"><![endif]-->
<!--[if !mso]><!-->
<td class="col" style="display:inline-block; width:100%; max-width:200px; vertical-align:top;">
<!--<![endif]-->
<div style="padding:16px;">Column 3</div>
</td>
</tr>
</table>Sources
- Can I email... display: flex
- Can I email... align-items
- Can I email... @media
- Can I email... gap, column-gap, row-gap
- Email on Acid: 12 things you must know about developing for Gmail and Gmail mobile apps
- Litmus: How to stack columns in responsive emails
- Litmus: HTML email background colors, the best way to code them
Postcraft's row block generates the full table-based layout above automatically. Pick 1, 2, or 3 columns, drop in your content, and the render API handles the pixel math and client compatibility, including the Outlook fallback paths.
Try it free