Fixing Line-Height Inconsistencies in Outlook 2016-2021
Stop the Word engine recomputing leading from font metrics: absolute line-height, mso-line-height-rule exactly, and the elements that need it beyond paragraphs.
A paragraph set at line-height: 1.5 renders with visibly tighter leading in Outlook 2016 through 2021 on Windows than in every other client, and a heading set the same way sometimes renders looser. This guide covers the exact declarations that pin leading in the Word engine, and the elements people forget to apply them to.
Root Cause: The Word Engine Treats Leading as a Minimum
Browsers compute a unitless line-height by multiplying it against the element's font size and using the result as the line box height. The Word engine does something different: it derives its own leading from the font's internal ascent, descent and line-gap metrics, then treats your declared value as a minimum rather than as the answer.
Two consequences follow. Where the font's natural leading exceeds your declared value, the engine uses the font's — so a heading at line-height: 1.1 renders looser than designed. Where the font's natural leading is smaller, the engine may still apply its own rounding, producing a value close to but not equal to yours. Neither behaviour is a bug in the sense of being unintended; the engine is laying out a word-processing document, where the font's metrics are authoritative.
Unitless values make this worse because there is nothing absolute to anchor on, and the fallback font — which the Word engine has probably substituted anyway, since it ignores @font-face entirely — has different metrics from the one you designed against.
The Exact Fix
Two declarations together, on every element that renders text.
<!-- line-height in ABSOLUTE units gives the engine a concrete value.
mso-line-height-rule:exactly changes it from a floor to an exact value,
so the font's own metrics stop contributing.
Affects: Outlook 2016, 2019, 2021 on Windows. Ignored elsewhere. -->
<td style="font-family:Arial,sans-serif;
font-size:16px;
line-height:24px;
mso-line-height-rule:exactly;
padding:0;">
Body copy that holds a 24px rhythm in every client.
</td>
mso-line-height-rule: exactly is the declaration that does the real work; the absolute unit is what gives it something unambiguous to apply. Using one without the other leaves the behaviour partially undefined: a pixel value alone is still treated as a minimum, and the MSO rule with a unitless value has no fixed number to enforce.
Because the rule is a mso- prefixed property, it is ignored by every non-Microsoft client, so it can be declared inline without a conditional wrapper. That is a considerable convenience — unlike most Word-engine fixes, this one needs no separate conditional block.
The Elements People Miss
The declaration inherits poorly in email, and several clients reset font properties on table elements, so it must be repeated rather than set once on a wrapper.
| Element | Needs the pair? | Why |
|---|---|---|
td containing text |
Yes | The most common text container; inheritance is unreliable |
p |
Yes | Some clients apply a default margin and leading |
h1–h6 |
Yes | Tight heading leading is where the floor behaviour is most visible |
a styled as a button |
Yes | Line height determines the button's rendered height |
span inside a styled cell |
Usually not | Inherits from the cell if the cell declares it |
li |
Yes | List items are frequently reset by the client |
Empty spacer td |
Yes | An empty cell with no leading rule collapses differently per engine |
The spacer cell row is the one that produces mysterious vertical gaps. An empty <td height="24"> with no line-height picks up the engine's default leading, which can exceed the declared height and push the layout apart. Setting line-height: 24px; mso-line-height-rule: exactly; font-size: 1px; on spacers makes them behave identically everywhere.
Variant: Outlook 365 and the New Outlook
Builds running on WebView2 — the new Outlook for Windows, and Microsoft 365 desktop after the rollout — use a Chromium-derived engine and honour line-height exactly as a browser does. They also ignore mso-line-height-rule, harmlessly.
This means a single template with both declarations renders correctly across all Outlook builds without a conditional. It also means a bug reported by a colleague on the new Outlook and not reproducible on Outlook 2019 is genuinely a different engine, not a version difference in the same one.
Diagnosing It Without a Windows Machine
The fix is easy; confirming it worked usually is not, because the engine that misbehaves is the one most developers cannot open locally. Three approaches narrow the gap.
The cheapest is arithmetic. Measure the rendered block height in a client that behaves correctly, divide by the number of lines, and compare against the declared leading. If a five-line paragraph at 24px leading occupies 120px in Apple Mail, the same paragraph occupying 145px in a client render farm screenshot is being laid out at roughly 29px per line — which is the font's natural leading rather than yours. You do not need to inspect the DOM to establish that; the pixel height is sufficient evidence.
The second is a deliberate probe. Add a temporary block to a test template with a leading value far outside any font's natural range — say 60px on 16px text — and send it. If the rendered block is 60px per line, the exact rule is being applied; if it is closer to 19px, the declaration is not reaching the element at all, which points at the inliner or a stripped style block rather than at the engine.
The third is the client render farm, which gives you screenshots from real Outlook builds and is the only way to see the actual result. Its cost is latency rather than money — a render takes minutes, which is fine for a pre-release check and too slow for iterating on a fix. Use the arithmetic method while iterating and the farm to confirm.
What none of these give you is a computed-style inspector, because the Word engine does not expose one. That is the underlying reason this class of bug is diagnosed by measurement rather than by inspection, and why a probe with an absurd value is often the fastest route to an answer.
Pipeline Integration
Put the pair in a component rather than in each template. A cell component that always emits font-family, font-size, line-height and mso-line-height-rule together makes it impossible to declare one without the others — which is the actual failure mode, since a developer adding a font size to an existing cell rarely thinks to add a leading rule alongside it.
Add a lint step over compiled output that flags any line-height without an adjacent mso-line-height-rule, and any unitless line-height at all. Both are mechanical checks that run in a fraction of a second and catch the regression at the point it is introduced, in the same pass as the rest of your markup checks.
Validation and Deployment Checklist
Frequently Asked Questions
Does mso-line-height-rule need a conditional comment?
No. It is a Microsoft-prefixed property, so every other client's CSS parser discards it as unknown. Declaring it inline alongside the standard line-height is safe and is the simplest way to ship it.
Why does my heading render looser rather than tighter?
Because the engine takes the larger of the font's natural leading and your declaration. A heading with a tight declared leading — anything below about 1.2 — is usually below the font's natural value, so the engine uses the font's instead. The exact rule is what overrides that.
Is line-height in em acceptable instead of pixels?
It is better than unitless but still not reliable, because em resolves against a font size that the Word engine may itself have substituted. Pixels remove the dependency entirely, which is the point of the fix.
Does this change how the message looks in clients that were already correct?
No. line-height: 24px and line-height: 1.5 on 16px text resolve to the same value in every engine that computes it properly, so converting from a multiplier to an absolute value is visually neutral outside the Word engine. The MSO property is discarded by those clients entirely. The change is therefore safe to apply across an existing template set without a visual review of every client — only Outlook's rendering moves, and it moves toward what you designed.
Should the fix be applied to preheader text too?
Yes, and it matters there more than elsewhere. Preheader text is usually hidden with a zero or one pixel font size, and without an exact leading rule the engine can still reserve a full line box for it — producing a mysterious gap at the very top of the message, above the visible content.
Related
- Email Typography and Web Fonts — the wider type system these declarations belong to
- How to Fix Outlook Table Spacing Issues — the other half of unexplained vertical drift in Outlook
- Outlook Rendering Fixes — the full set of Word-engine overrides
- Email Design Tokens and Component Libraries — where the cell component that emits these declarations lives
← Back to Email Typography and Web Fonts