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.
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:
- Apple Mail (iOS + macOS)
- Outlook (iOS + Android)
- Gmail (web)
- Yahoo (web)
- Thunderbird
- 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.
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.
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.
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.
inline-block reliably. If Outlook desktop is critical for your audience, use MSO conditional comments for a table-based fallback:// 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.
// 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.
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%withmaxWidth: 300pxmakes 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.
Pattern 5: Responsive images
Images are the most common cause of horizontal scrolling in email. The fix is simple but must be applied consistently.
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",
}}
/>
);
}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.
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>
);
}!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:
- React Email dev server: First pass — check layout at different widths in the browser preview.
- Send test emails to real devices: Your phone, your partner's phone, a tablet. Real devices catch touch target issues that emulators miss.
- Email testing services: Litmus or Email on Acid for cross-client screenshots. Essential for Outlook desktop validation.
- Automated visual regression: Playwright tests at 375px and 800px viewports in CI. Catches regressions between PRs.
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.