FlowLibs
Browse Cloud FlowsConnectorsAI ToolsDev ToolsPricingAboutContact
Dev Tools/HTML Email

HTML Email Toolkit

Build emails that survive Outlook. A live preview gallery, a battle-tested responsive boilerplate, VML bulletproof buttons, responsive + accessibility + deliverability guidance, and the rules for sending rich HTML from Power Automate’s “Send an email (V2)” action.

Email16 min read·Updated 2026-06-16
HTML EmailOutlookVMLSend an email V2Office 365ResponsiveAccessibilityDeliverability
Template gallery

Status update with a single call-to-action button.

On this page

  • Why email HTML is its own thing
  • Responsive boilerplate
  • Send an email (V2)
  • Dynamic rows & tables
  • Taming Outlook on Windows
  • Responsive & mobile
  • Images that actually show up
  • Accessibility
  • Dark mode & testing
  • Deliverability & inbox placement
  • Tooling & workflow

Why email HTML is its own thing

Email clients are 15 years behind browsers. Desktop Outlook on Windows renders with Microsoft Word’s engine — no flexbox, no CSS grid, patchy float, and <style> blocks are often stripped. Treat email like 2009 web: tables for layout, inline CSS for styling, fixed widths.

  • Lay out with nested <table> elements, not <div> + flex.
  • Put styles inline (style="…") — many clients drop <head><style>.
  • Keep the body ≤ 600px wide; phones scale down, desktops do not stretch.
  • Use web-safe fonts (Arial, Helvetica, Georgia) with a stack fallback.
  • Always set role="presentation", cellpadding="0", cellspacing="0", border="0" on layout tables.

Try the template gallery

The gallery at the top of this page previews ready-made templates — notification, approval, receipt, digest, reminder, alert, two-column, hero, newsletter, OTP, calendar invite, survey, and welcome — in a sandboxed frame. Switch to Source to tweak any of them, then copy it straight into your flow.

Responsive boilerplate

A single-column, centered shell that holds up across Outlook, Gmail, and Apple Mail. Drop your content into the inner cell.

html
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"
       style="background:#f4f5f7;padding:24px 0;">
  <tr>
    <td align="center">
      <table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0"
             style="width:600px;max-width:600px;background:#ffffff;border-radius:8px;
                    font-family:Arial,Helvetica,sans-serif;color:#1a1a1a;">
        <tr>
          <td style="padding:28px 32px 8px;font-size:20px;font-weight:bold;">
            Request received
          </td>
        </tr>
        <tr>
          <td style="padding:8px 32px 24px;font-size:14px;line-height:22px;color:#444;">
            Hi @{triggerBody()?['name']}, we logged your request and will reply
            within one business day.
            <br><br>
            <!-- bulletproof button -->
            <table role="presentation" cellpadding="0" cellspacing="0" border="0">
              <tr>
                <td bgcolor="#2563eb" style="border-radius:6px;">
                  <a href="https://flowlibs.com"
                     style="display:inline-block;padding:11px 22px;font-size:14px;
                            font-weight:bold;color:#ffffff;text-decoration:none;">
                    View status
                  </a>
                </td>
              </tr>
            </table>
          </td>
        </tr>
        <tr>
          <td style="padding:16px 32px;border-top:1px solid #eee;font-size:12px;color:#888;">
            Sent by FlowLibs · This is an automated message.
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>
Paste into Send an email (V2) → Body → code view (</>).

Send an email (V2)

  1. In Office 365 Outlook → Send an email (V2), click the </> (code view) toggle on the Body field before pasting HTML — otherwise the editor escapes your tags.
  2. Inject dynamic values with expressions inside the markup, e.g. @{triggerBody()?['name']} or @{outputs('Compose')}.
  3. Set Importance, CC/BCC, and Reply To under *Advanced parameters*.
  4. Attach files (including a .ics calendar invite) by passing an array of { Name, ContentBytes } to the Attachments parameter.

Switch to code view first

If pasted HTML shows up as literal <table> text in the received email, you pasted into the rich-text editor. Toggle </> code view, clear the field, and paste again.

Dynamic rows & tables

For a quick grid use the built-in Create HTML table action. For full control over styling, build rows yourself: Select to shape each row’s HTML, then join() into the body.

text
<tr><td style="padding:6px 10px;border-bottom:1px solid #eee">@{item()?['Title']}</td>
<td style="padding:6px 10px;border-bottom:1px solid #eee">@{item()?['Status']}</td></tr>
Select action — Map (text mode) produces one <tr> per item.
text
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
  @{join(body('Select'), '')}
</table>
Then in the email body, concatenate the joined rows:

Taming Outlook on Windows

Desktop Outlook (2016–2024 + the classic client) renders with Word, so it needs its own workarounds wrapped in <!--[if mso]> conditional comments. These are invisible to every other client, so you can ship one template that satisfies both.

VML bulletproof button (roundrect)

A plain padded <a> collapses to just the text in Outlook — only the words are clickable and the rounded background disappears. A VML roundrect draws a real button Outlook can render; the !mso block serves the normal HTML button to everyone else.

html
<!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
             xmlns:w="urn:schemas-microsoft-com:office:word"
             href="https://flowlibs.com"
             style="height:44px;v-text-anchor:middle;width:200px;"
             arcsize="14%" strokecolor="#2563eb" fillcolor="#2563eb">
  <w:anchorlock/>
  <center style="color:#ffffff;font-family:Arial,sans-serif;font-size:14px;font-weight:bold;">
    View status
  </center>
</v:roundrect>
<![endif]-->
<!--[if !mso]><!-- -->
<a href="https://flowlibs.com"
   style="display:inline-block;background:#2563eb;border-radius:6px;padding:12px 24px;
          font-family:Arial,sans-serif;font-size:14px;font-weight:bold;
          color:#ffffff;text-decoration:none;">View status</a>
<!--<![endif]-->
One button, two renderings — Outlook gets VML, everyone else gets HTML.

Ghost tables for width & centering

Outlook ignores max-width and margin:0 auto. Wrap fluid content in an mso-only fixed-width table — the “ghost table” — so Outlook gets a hard 600px column while modern clients use the responsive <div>.

html
<!--[if mso]>
<table role="presentation" align="center" width="600" cellpadding="0" cellspacing="0" border="0">
<tr><td>
<![endif]-->
<div style="max-width:600px;margin:0 auto;">
  <!-- fluid content here -->
</div>
<!--[if mso]>
</td></tr></table>
<![endif]-->
Ghost table — fixed width for Outlook, fluid div for the rest.

VML background image

html
<!--[if mso]>
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false"
        style="width:600px;height:200px;">
  <v:fill type="frame" src="https://cdn.flowlibs.com/hero.png" color="#1e3a8a" />
  <v:textbox inset="0,0,0,0">
<![endif]-->
<div><!-- overlay text/buttons here --></div>
<!--[if mso]></v:textbox></v:rect><![endif]-->
CSS background-images are dropped by Outlook — paint them with VML instead.
  • Set mso-line-height-rule:exactly (and an explicit line-height) or Outlook adds extra vertical space.
  • Use cell padding, never margin — Outlook drops margin on most elements.
  • Add mso-table-lspace:0pt;mso-table-rspace:0pt; to tables to kill phantom side gaps.
  • Force the font with an mso-prefixed fallback; Outlook ignores web fonts entirely.
  • Apply a page background to both <body> and the outermost table — Outlook only honours the table.

Conditional comments are mso-only

Anything inside <!--[if mso]>…<![endif]--> renders only in Windows Outlook. Apple Mail, Gmail, and Outlook for Mac/web treat it as a comment and skip it — so keep your real fallback in the !mso branch.

Responsive & mobile

Over half of opens are on a phone. Two strategies: media queries (clean, but ignored by Windows Outlook and stripped by some Gmail contexts) and the hybrid/fluid technique (works almost everywhere because it needs no <style> block).

Mobile stacking with media queries

html
<style>
  @media only screen and (max-width:600px) {
    .col { width:100% !important; display:block !important; }
    .px  { padding-left:20px !important; padding-right:20px !important; }
    .h1  { font-size:22px !important; }
  }
</style>
<!-- mark the cells you want to stack -->
<td class="col" width="50%" valign="top">…</td>
<td class="col" width="50%" valign="top">…</td>
Head <style> — columns go full-width below 600px. Outlook ignores this (it stays desktop).

The hybrid (spongy) technique

Instead of media queries, set columns to display:inline-block with a max-width, then wrap each in a ghost table (see the Outlook section). The cells flow side by side when there is room and wrap to a single column when there is not — no <style> required, so it survives clients that strip the head.

TechniqueOutlook (Win)Outlook.comApple MailGmail (web/app)
<style> + media queriesNoPartialYesYes (strips some)
Background image (CSS)VML onlyYesYesYes
Web fontsNo (fallback)NoYesNo (fallback)
border-radiusNoYesYesYes
Padding on <a>Needs VMLYesYesYes
max-width fluid layoutGhost tableYesYesYes
Approximate support — always confirm with a real test. “VML only” / “Ghost table” work in Outlook but need the mso fallback above.

Design unstacked-first

Because Outlook never stacks, your two-column layout must already look right side by side at 600px. Treat mobile stacking as progressive enhancement, not the baseline.

Images that actually show up

  • Host on an HTTPS CDN and link with an absolute URL. Do not embed base64/cid data URIs — Outlook strips them and they balloon the message size.
  • Set explicit width and height attributes so the layout does not jump while images load (and so Outlook reserves the space).
  • Retina: export at 2× the display size, then constrain with the width attribute/style — e.g. a 1200px-wide file shown at width:600.
  • Always give meaningful alt text; use alt="" for purely decorative images so screen readers skip them.
  • Most clients block images by default. Style the blocked state — set a bgcolor on the image cell and a visible text colour so the alt text reads cleanly against it.
  • Keep critical content (offers, instructions, CTAs) as live text, never baked into an image — image-only emails fail when images are blocked and hurt deliverability.

The base64 caveat

Inlining images as data: URIs feels convenient and renders in this page’s sandboxed preview, but Gmail and Outlook strip or ignore them, and they push you toward the 102KB clip limit. Host real images.

Accessibility

Accessible email is also better email — clearer hierarchy, higher contrast, and content that survives image blocking. A few rules cover most of it.

  • Put role="presentation" on every layout table so assistive tech treats it as a wrapper, not a data grid.
  • Set a language on the wrapper (lang="en") so screen readers pronounce content correctly.
  • Order your source markup in reading order — screen readers and Gmail’s linearizer follow source order, not visual position.
  • Give images real alt text (or alt="" when decorative); never describe a button only by colour.
  • Hit at least 4.5:1 contrast for body text; do not signal meaning with colour alone (pair it with a label or icon).
  • Use aria-hidden="true" on decorative spacers and glyphs, and write descriptive link text (“View invoice”, not “click here”).
  • Keep body copy at 14px+ with line-height around 1.5; tiny text is the most common accessibility miss in email.

Preheader does double duty

A good preheader (see Deliverability) is read aloud right after the subject by screen readers — write it as a real summary sentence, not filler.

Dark mode & testing

  • Declare <meta name="color-scheme" content="light dark"> and <meta name="supported-color-schemes" content="light dark"> so clients know you handled both.
  • Avoid pure-white logos on transparent PNGs — they vanish on dark backgrounds. Add a subtle stroke/halo, or swap a dark-mode logo via a media query.
  • Some clients (Outlook.com, Apple Mail, parts of Gmail) auto-invert colours. Pin critical brand colours and test that inverted text stays legible.
  • Watch buttons and bordered callouts — an inverted background can erase a border or wash out white button text.
  • Test the real send in Outlook (Windows + web), Gmail (web + mobile app), and Apple Mail, in both light and dark, before shipping a template into production flows.

Deliverability & inbox placement

Rendering is half the battle; the other half is landing in the inbox and reading well in the preview pane.

Preheader text

The preheader is the grey snippet shown after the subject line. If you do not set one, clients grab the first text they find — often “View in browser” or a raw link. Hide a real summary sentence at the very top of <body>, padded so no stray body text leaks in.

html
<div style="display:none;max-height:0;overflow:hidden;mso-hide:all;
            font-size:1px;line-height:1px;color:#ffffff;">
  Your request was received — we reply within one business day.
  &zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;
</div>
First element inside <body> — hidden, but it owns the inbox preview snippet.
  • Authentication (SPF, DKIM, DMARC) is DNS/tenant config. With Send an email (V2) your M365 tenant sends, so alignment is usually handled; for custom domains or SMTP, confirm all three pass or you will land in spam.
  • List-Unsubscribe: bulk/marketing mail should set the List-Unsubscribe (and one-click) header — this is not exposed by Send an email V2, so use a sending platform or Graph for true bulk. Always include a visible unsubscribe link too.
  • Gmail clips messages over ~102KB (“…[Message clipped]”) — keep markup lean, avoid base64 images, and put the important content and unsubscribe link above the cut.
  • Avoid spam triggers: all-image emails, ALL CAPS / excessive !!!, link shorteners, and visible link text whose href points somewhere else.
  • Send a plain-text alternative (multipart) — it improves placement and serves text-only clients. Send an email V2 auto-generates one from your HTML; for hand-tuned text use Graph or SMTP multipart.

Tooling & workflow

  • MJML — author in a concise, component-based syntax and compile to bulletproof table HTML (it emits the VML/ghost-table boilerplate for you).
  • Litmus / Email on Acid — render previews across 90+ real clients and catch Outlook/Gmail breakage before a template hits production flows.
  • CSS inliners (Juice, Premailer, or MJML’s built-in inliner) move <style> rules inline so they survive clients that strip the head.
  • Plain-text alternative — keep a hand-written .txt version of important templates rather than relying solely on auto-generation.
  • RTL / localization — set dir="rtl" on the wrapper for Arabic/Hebrew and mirror padding; keep copy as live text (not images) so it can be translated, and set lang per locale.

Build a library, not one-offs

Save one tested base template (boilerplate + bulletproof button + preheader) in your FlowLibs library and clone it per flow. Test once, reuse everywhere — and keep the gallery on this page as your starting points.

← All Dev ToolsUse these in your AI assistant →
Spotted an error or have a suggestion? Let us know
FlowLibs

A curated library of production-grade Power Automate cloud flow patterns. Packaged as managed solutions, ready to import into your environment.

Library

  • Browse Cloud Flows
  • Approvals
  • Email & Notifications
  • Reporting
  • Security & Compliance

AI

  • AI Tools
  • MCP Server
  • Generate a Token

Resources

  • About
  • FAQ
  • Support
  • Status
  • Contact
  • Power Automate Docs
  • Connector Reference

© 2026 FlowLibs. All rights reserved.

  • Privacy
  • Terms
  • Refunds
  • Cookies
  • Acceptable Use
  • DMCA
Help