React Email11 min read

Responsive Email Design Patterns with React Email

Over 60% of emails are opened on mobile, yet most templates are desktop-first. Learn fluid layouts, stacking columns, touch-friendly CTAs, and the CSS tricks that work across 90+ email clients.

R

React Emails Pro

March 7, 2026

Over 60% of emails are opened on mobile. Yet most email templates are still designed desktop-first — wide tables, fixed-width columns, tiny buttons that require precision tapping. The result: users pinch-zoom your password reset email on their phone, miss the CTA, and give up.

Responsive email design is harder than responsive web design. CSS support across email clients is wildly inconsistent. Media queries work in some clients but not others. And the fallback — a desktop layout rendered at mobile width — is almost always broken.

This guide covers practical responsive patterns for React Email: fluid widths, stacking columns, mobile-first typography, touch-friendly CTAs, and the clients that break your assumptions.

62%

Mobile email opens

As of 2025 (Litmus Email Analytics)

44px

Min touch target

Apple HIG recommended minimum

~30%

Ignore media queries

Gmail app, Outlook desktop, Yahoo


Email client CSS support (the uncomfortable truth)

Before writing any responsive code, you need to understand what works where:

Supports media queries
  • Apple Mail (iOS + macOS)
  • Outlook (iOS + Android)
  • Gmail (web)
  • Yahoo (web)
  • Thunderbird
Ignores media queries
  • Gmail app (Android + iOS) — strips <style> blocks
  • Outlook desktop (Windows) — Word rendering engine
  • Outlook.com (web) — partial support only
  • Yahoo app (mobile)
  • Some corporate Exchange clients

This means your email must look good without media queries. Responsive enhancements are progressive — they improve the experience where supported, but the base layout must work everywhere.

Key takeaway

Design for the worst case (Gmail app: no media queries, no <style> block), then enhance for the best case (Apple Mail: full CSS support). This is the only strategy that works across 90+ email clients.


Pattern 1: Fluid container width

The most impactful responsive pattern is also the simplest: use a percentage-based max-width with a pixel cap.

emails/components/base-layout.tsx
import { Html, Head, Body, Container } from "@react-email/components";

export function BaseLayout({ children }: { children: React.ReactNode }) {
  return (
    <Html>
      <Head />
      <Body style={{
        backgroundColor: "#f6f9fc",
        fontFamily: '-apple-system, "Segoe UI", Roboto, sans-serif',
        margin: 0,
        padding: 0,
      }}>
        <Container style={{
          maxWidth: "560px",   // Cap on desktop
          width: "100%",       // Fluid on mobile
          margin: "0 auto",
          padding: "24px 16px", // Side padding prevents edge-to-edge on mobile
        }}>
          {children}
        </Container>
      </Body>
    </Html>
  );
}
padding: "24px 16px" on the container is critical. Without horizontal padding, your text hits the screen edge on mobile. 16px gives comfortable breathing room on small screens.

Pattern 2: Columns that stack on mobile

Two-column layouts are common in emails (feature comparisons, pricing tables, image + text). They need to stack vertically on narrow screens.

The inline-block stacking approach

This technique works without media queries. It relies on display: inline-block and min-width to naturally wrap columns when the viewport gets narrow.

emails/components/responsive-columns.tsx
import { Section } from "@react-email/components";

type TwoColumnProps = {
  left: React.ReactNode;
  right: React.ReactNode;
};

export function TwoColumn({ left, right }: TwoColumnProps) {
  const columnStyle: React.CSSProperties = {
    display: "inline-block",
    verticalAlign: "top",
    width: "100%",
    maxWidth: "260px",  // Each column is half of 560px - gutters
    minWidth: "200px",  // Forces stack below this width
    padding: "0 8px",
  };

  return (
    <Section style={{ textAlign: "center" }}>
      {/* Using divs with inline-block for natural wrapping */}
      <div style={columnStyle}>{left}</div>
      <div style={columnStyle}>{right}</div>
    </Section>
  );
}

The trick: when the container narrows below ~400px, each column's minWidth: 200px forces them to wrap naturally. No media queries needed.

Outlook desktop renders with the Word engine, which doesn't support inline-block reliably. If Outlook desktop is critical for your audience, use MSO conditional comments for a table-based fallback:
emails/components/outlook-columns.tsx
// Outlook-safe two-column with MSO conditionals
export function OutlookSafeColumns({ left, right }: TwoColumnProps) {
  return (
    <Section>
      {/* MSO conditional: table layout for Outlook */}
      <div dangerouslySetInnerHTML={{ __html: '<!--[if mso]><table role="presentation" width="100%"><tr><td width="50%" valign="top"><![endif]-->' }} />
      <div style={{
        display: "inline-block",
        verticalAlign: "top",
        width: "100%",
        maxWidth: "268px",
      }}>
        {left}
      </div>
      <div dangerouslySetInnerHTML={{ __html: '<!--[if mso]></td><td width="50%" valign="top"><![endif]-->' }} />
      <div style={{
        display: "inline-block",
        verticalAlign: "top",
        width: "100%",
        maxWidth: "268px",
      }}>
        {right}
      </div>
      <div dangerouslySetInnerHTML={{ __html: '<!--[if mso]></td></tr></table><![endif]-->' }} />
    </Section>
  );
}

Pattern 3: Mobile-first typography

Font sizes that look fine on desktop become unreadable on mobile. And sizes that work on mobile can feel oversized on desktop. The solution: set sizes that work on both, then optionally fine-tune with media queries.

emails/tokens.ts
// Typography scale that works across desktop and mobile
// without media queries
export const typography = {
  heading: {
    fontSize: "24px",      // Readable on mobile, not oversized on desktop
    lineHeight: "32px",
    fontWeight: 600,
  },
  subheading: {
    fontSize: "18px",
    lineHeight: "26px",
    fontWeight: 600,
  },
  body: {
    fontSize: "16px",      // 16px is the sweet spot — readable everywhere
    lineHeight: "26px",    // 1.625 ratio for comfortable reading
    fontWeight: 400,
  },
  caption: {
    fontSize: "14px",      // Minimum for mobile readability
    lineHeight: "20px",
    color: "#6b7280",
  },
  // AVOID: 13px or smaller — too small on mobile
  // AVOID: 28px+ headings — feels shouty on small screens
} as const;
  • Body text: 16px minimum. Anything smaller requires zooming on most phones. iOS will auto-zoom inputs below 16px.
  • Line height: 1.5-1.625x. Tighter line heights that work on desktop become cramped on mobile.
  • Headings: 24px max. Larger headings overflow on 320px screens or cause awkward wrapping.

Pattern 4: Touch-friendly CTAs

The most important responsive pattern for conversion. If your CTA button is too small to tap confidently, nothing else matters.

emails/components/responsive-button.tsx
import { Button } from "@react-email/components";

type ResponsiveButtonProps = {
  href: string;
  children: React.ReactNode;
  variant?: "primary" | "secondary";
};

export function ResponsiveButton({
  href,
  children,
  variant = "primary",
}: ResponsiveButtonProps) {
  const isPrimary = variant === "primary";

  return (
    <Button
      href={href}
      style={{
        display: "inline-block",
        backgroundColor: isPrimary ? "#18181b" : "transparent",
        color: isPrimary ? "#ffffff" : "#18181b",
        border: isPrimary ? "none" : "2px solid #d4d4d8",
        borderRadius: "8px",
        padding: "14px 28px",       // 44px+ touch target height
        fontSize: "16px",           // Readable without zooming
        fontWeight: 600,
        textDecoration: "none",
        textAlign: "center" as const,
        // Full-width on mobile via max-width trick
        width: "100%",
        maxWidth: "300px",
      }}
    >
      {children}
    </Button>
  );
}
  • Minimum 44px height: Apple's Human Interface Guidelines minimum. 14px padding top + bottom + 16px font + line height gets you there.
  • Full width on mobile: width: 100% with maxWidth: 300px makes the button fill the screen on mobile but stays contained on desktop.
  • 16px font minimum: CTA text must be readable without zooming. 14px CTAs on mobile are a conversion killer.

Recommended

SaaS Essentials Pack

21+ Templates · 60+ Variations. One-time purchase, lifetime updates.

$19.95$9.95Get it

Pattern 5: Responsive images

Images are the most common cause of horizontal scrolling in email. The fix is simple but must be applied consistently.

emails/components/responsive-image.tsx
import { Img } from "@react-email/components";

type ResponsiveImgProps = {
  src: string;
  alt: string;
  width: number;
  height: number;
};

export function ResponsiveImg({ src, alt, width, height }: ResponsiveImgProps) {
  return (
    <Img
      src={src}
      alt={alt}
      width={width}
      height={height}
      style={{
        display: "block",
        maxWidth: "100%",  // Never overflow container
        height: "auto",    // Maintain aspect ratio
        border: 0,
        outline: "none",
        textDecoration: "none",
      }}
    />
  );
}
Always set both width and height attributes on images. Email clients use these for layout calculations before the image loads. Without them, content jumps when images load (or doesn't layout correctly if images are blocked).

Progressive enhancement with media queries

Media queries are a bonus for clients that support them — not a requirement. Use them to fine-tune the experience in Apple Mail, iOS Outlook, and Gmail web.

emails/components/base-layout.tsx
import { Html, Head, Body, Container } from "@react-email/components";

export function BaseLayout({ children }: { children: React.ReactNode }) {
  return (
    <Html>
      <Head>
        <style dangerouslySetInnerHTML={{ __html: `
          /* Only applied in clients that support <style> blocks */
          @media only screen and (max-width: 480px) {
            .email-container {
              padding: 16px 12px !important;
            }
            .heading {
              font-size: 22px !important;
            }
            .button-full-width {
              width: 100% !important;
              max-width: 100% !important;
              text-align: center !important;
            }
            .hide-on-mobile {
              display: none !important;
              max-height: 0 !important;
              overflow: hidden !important;
            }
            .stack-on-mobile {
              display: block !important;
              width: 100% !important;
              max-width: 100% !important;
            }
          }
        `}} />
      </Head>
      <Body style={{ backgroundColor: "#f6f9fc", margin: 0 }}>
        <Container className="email-container" style={{
          maxWidth: "560px",
          width: "100%",
          margin: "0 auto",
          padding: "24px 16px",
        }}>
          {children}
        </Container>
      </Body>
    </Html>
  );
}
Use !important in media queries. Email clients that inline styles (like Gmail web) will override your class-based styles without it. This is one of the rare cases where !important is the correct approach.

Testing responsive emails

You can't test responsive emails by resizing your browser. Email clients render HTML differently from browsers. Here's the testing stack that actually works:

  1. React Email dev server: First pass — check layout at different widths in the browser preview.
  2. Send test emails to real devices: Your phone, your partner's phone, a tablet. Real devices catch touch target issues that emulators miss.
  3. Email testing services: Litmus or Email on Acid for cross-client screenshots. Essential for Outlook desktop validation.
  4. Automated visual regression: Playwright tests at 375px and 800px viewports in CI. Catches regressions between PRs.
If you can only test on 3 clients, test on: Gmail app (Android), Apple Mail (iOS), and Outlook desktop (Windows). These represent the three rendering engines that matter: stripped HTML, WebKit, and Word.

Responsive email checklist

  • Container: maxWidth: 560px + width: 100% + side padding
  • Body text: 16px minimum, 26px line height
  • Headings: 24px maximum to prevent overflow
  • CTA buttons: 44px+ touch target, 16px font, full-width on mobile
  • Images: maxWidth: 100% + explicit width/height attributes
  • Columns: inline-block with minWidth for natural stacking
  • Media queries: progressive enhancement only, with !important
  • Outlook: MSO conditionals for table-based column fallbacks
  • Test on Gmail app, Apple Mail iOS, and Outlook desktop at minimum

Responsive email design is about pragmatism, not perfection. You won't achieve pixel-perfect consistency across 90+ clients. The goal is: readable text, tappable buttons, no horizontal scrolling. Nail those three and your emails work on every device that matters.

R

React Emails Pro

Team

Building production-ready email templates with React Email. Writing about transactional email best practices, deliverability, and developer tooling.

Production-ready templates

Pick from 9 template packs built with React Email. One-time purchase, lifetime updates, tested across every major email client.

Browse all templates