Get in Touch
React Native HTML Email: How to Render Email Content in Mobile Apps - Red Surge Technology Blog
React NativeEmailMobile DevelopmentJavaScriptHTML

Building an email client in React Native sounds straightforward until you actually try to render the emails. Email HTML is a different species from web HTML. It's built on tables, inline styles, and a decade of workarounds for email clients that never agreed on anything. Outlook uses Word's rendering engine. Gmail strips <style> tags in some contexts. Apple Mail supports modern CSS but only sometimes.

Now try rendering that same HTML in React Native, which has no DOM, no CSS engine, and no concept of a <table> element. React Native uses Yoga, a Flexbox implementation, and it has no idea what to do with the table-based layouts that make up 90% of marketing emails.

I've built email viewing features in React Native apps. The first time I tried, I assumed I could drop in a library and call it a day. The first version looked fine with my test emails. Then real emails arrived—newsletters with multi-column layouts, transactional emails with nested tables, marketing emails with background images on table cells—and everything fell apart. This guide is what I wish I'd had at the start.

Why Email HTML Is Hard in React Native

The core issue is that React Native doesn't render HTML. It renders native components. A <View> is a UIView on iOS or an Android View. A <Text> maps to a native text element. There's no browser engine parsing your markup and laying out elements. Everything has to be translated into React Native components.

For simple HTML—paragraphs, headings, bold text, links—the translation is straightforward. A <p> becomes a <Text> with some styles. An <a> becomes a <Text> with an onPress handler. Easy.

Email HTML is not simple. The average marketing email uses:

  • Table-based layouts. Nested tables inside tables, often three or four levels deep, to create multi-column designs that work in Outlook.
  • Inline styles on everything. Because email clients strip <style> blocks, all styling is inline. style="color: #333; font-family: Arial; font-size: 14px; line-height: 1.5;" on every element.
  • Background images on table cells. Used for hero sections, buttons, and decorative elements.
  • Conditional comments. <!--[if mso]>...<![endif]--> blocks that only Outlook parses.
  • Fixed pixel widths. width="600" on the main table, because email clients don't handle responsive layouts consistently.

When you feed this into a native HTML renderer, the renderer has to make sense of it. Tables become flex containers, which don't behave the same way. Inline styles get parsed and mapped to React Native styles, which support a subset of CSS. Background images on table cells are ignored because React Native doesn't support them on <View>.

The result is that many emails render incorrectly. Text overflows, columns collapse, images don't display, and the layout looks nothing like what the sender intended.

The Three Approaches to Rendering Email HTML

There are three main approaches, each with tradeoffs.

Approach 1: WebView

The WebView approach embeds a browser engine in your app and renders the email HTML inside it. This is the most faithful approach—the email renders exactly as it would in a mobile browser, which is the closest you'll get to the sender's intended design.

import { WebView } from "react-native-webview";

function EmailView({ html }) {
  return (
    <WebView originWhitelist={["*"]} source={{ html }} style={{ flex: 1 }} />
  );
}

Pros: Renders everything—tables, inline styles, background images, conditional comments (ignored gracefully). The email looks like it does in a browser.

Cons: Heavy. A WebView spins up an entire browser engine, consuming memory and CPU. Scrolling and touch handling can feel disconnected from the rest of your app. Security is a concern—you're loading untrusted HTML and JavaScript into a browser context. And styling the WebView to match your app's theme is fiddly.

For email clients specifically, WebView is often the pragmatic choice. Email HTML is hostile, and WebView handles hostile HTML better than native renderers. If you're building a full email client, WebView is probably what you'll end up with.

Approach 2: Native HTML Renderers

Native HTML renderers parse HTML and translate it into React Native components. There's no browser engine, no WebView, and the output looks and behaves like the rest of your app.

The leading option is react-native-render-html, which has been rebranded and maintained as @native-html/render by Software Mansion (the team behind Reanimated and Gesture Handler). It's TypeScript-native, supports a wide range of CSS properties, and includes custom renderers for overriding how any tag displays.

import RenderHtml from "@native-html/render";
import { useWindowDimensions } from "react-native";

function EmailView({ html }) {
  const { width } = useWindowDimensions();
  return (
    <RenderHtml
      contentWidth={width}
      source={{ html }}
      tagsStyles={{
        body: { fontSize: 16, lineHeight: 24, color: "#333" },
        a: { color: "#3b82f6" },
      }}
    />
  );
}

Pros: Lightweight, native feel, themable with your app's design system. No browser engine overhead.

Cons: Doesn't render everything. Tables are the biggest gap—more on that below. Background images on elements are ignored. Complex CSS is partially supported. For a simple transactional email (order confirmation, password reset), it works well. For a marketing newsletter with a complex layout, it struggles.

Another option is react-native-htmlview, which is lightweight and handles links and basic formatting. But it's not actively maintained, and its table support is minimal. The react-native-rich-html-renderer library is newer and supports auto-link detection, dark mode, and virtualized rendering with FlatList, but it's a smaller community project.

Approach 3: Server-Side Pre-Processing

A hybrid approach: pre-process the email HTML on the server before sending it to the app. Strip the table-based layout, extract the content, and return a simplified HTML or JSON structure that the native renderer can handle.

This is more work upfront, but it gives you control over what the app receives. You can normalize the HTML, remove unnecessary tables, inline only the styles that React Native supports, and handle images with proper dimensions.

The tradeoff is that you lose some fidelity. The email won't look exactly as the sender designed it, but it will render reliably and perform well. For many apps, that's an acceptable trade.

The Table Problem (And How to Handle It)

Tables are the biggest challenge. Email HTML relies on tables for layout, and React Native doesn't understand tables.

The issue is fundamental. HTML tables use a two-pass layout algorithm: first, the browser measures all cells to determine column widths; then it distributes remaining space and renders. React Native's Flexbox uses a single-pass algorithm: children size themselves, and parents adjust. A cell with width: auto in HTML expands to fit content and negotiates with siblings. A flex item with no explicit width compresses or expands based on flexGrow and flexShrink, ignoring content entirely.

The result is predictable chaos: columns collapse, text overflows, rows misalign. A table that looks correct in Safari becomes unreadable on iOS.

@native-html/render ships with a heuristic table plugin that guesses column widths. It works for simple tables but fails on complex ones. The plugin renders tables inside a WebView, which reintroduces the WebView overhead you were trying to avoid. There's also a minimal table renderer that doesn't follow CSS table display rules, but it's limited.

The pragmatic solution for email clients: if your emails have complex tables, use WebView. If your emails are mostly simple transactional content, use @native-html/render and accept that some layouts won't render perfectly.

If you're building a content-heavy app that happens to render HTML from a CMS—not email specifically—the table situation is less dire. CMS content is usually simpler and more modern than email HTML. Our guide on React Native Skia covers another approach to custom rendering when you need complete control.

CSS Support: What Works and What Doesn't

@native-html/render supports a wider range of CSS properties than most native renderers. It includes things like list-style-type, white-space, and text-decoration. But it's still a subset of CSS, and React Native's style system is not a browser's.

Here's a quick reference for what typically works and what doesn't in email HTML:

Works: Color, background-color, font-size, font-weight, font-style, text-align, margin, padding, border, border-radius, line-height, letter-spacing, text-decoration.

Partially works: Background images (on some elements), box-shadow (on iOS), flexbox-based layouts (if you can bypass the table structure).

Doesn't work: CSS grid, floats (ignored), positioning (limited), media queries (not in the same way), background images on table cells.

The practical takeaway: don't expect email HTML to render pixel-perfect in a native renderer. Expect it to render reasonably—readable, navigable, and functional. If exact visual fidelity is a requirement, WebView is the only way to achieve it.

Integrating with Email Services

Rendering is only half the problem. You also need to fetch the emails. Here's how the integration typically works.

Gmail API. The Gmail API returns email bodies as base64-encoded strings. You decode them and get HTML or plain text. The HTML often uses Gmail-specific classes and inline styles. It's usually fairly clean compared to marketing email HTML, which makes it a good candidate for native rendering.

// Fetching email body from Gmail API
const response = await gmail.users.messages.get({
  userId: "me",
  id: messageId,
  format: "full",
});

const parts = response.data.payload.parts;
const htmlPart = parts.find((p) => p.mimeType === "text/html");
const html = Buffer.from(htmlPart.body.data, "base64").toString("utf-8");

IMAP. Libraries like imapflow or node-imap (running on the server, not in the app) can fetch raw email content. The HTML body is typically in a MIME part, base64-encoded. You decode it and pass it to the renderer.

Transactional email APIs. Services like SendGrid, Postmark, and Resend provide APIs for sending emails and retrieving their content. The content is usually cleaner and more modern than marketing emails, which makes native rendering more viable.

If you're building the sending side—generating HTML emails in React—check our guide on React Email components. That covers the React Email library, which lets you build email templates as React components. The receiving side is a different problem entirely.

Security Considerations

Email HTML is untrusted HTML. It comes from external sources, and it can contain malicious JavaScript, tracking pixels, and phishing links. If you're rendering it in a WebView, you're loading that content into a browser context. That's a security risk.

For WebView: Disable JavaScript unless you have a specific need for it. Use originWhitelist={['*']} only if necessary, and consider restricting it. Block third-party tracking pixels by intercepting network requests and filtering by domain.

<WebView
  source={{ html }}
  javaScriptEnabled={false}
  originWhitelist={["about:blank"]}
  onShouldStartLoadWithRequest={(request) => {
    // Only allow navigation to trusted domains
    return request.url.startsWith("https://");
  }}
/>

For native renderers: Sanitize the HTML before rendering. Remove <script> tags, on* event handlers, and javascript: URLs. Libraries like sanitize-html work in React Native. Never trust email HTML directly.

For links: Intercept link taps and validate URLs before opening them. Block javascript: and data: URLs. Show the full URL to the user before navigating.

A Real Story: Building an Email Reader Feature

A few months ago, I worked on a mobile app that needed an email reading feature. Users received notifications and transactional emails from the platform, and the app needed to display them in a branded, in-app inbox.

I started with @native-html/render because the app already used it for CMS content, and it handled that content beautifully. For simple emails—order confirmations, password resets, notification digests—it worked well. The emails rendered natively, matched the app's design system, and performed smoothly.

Then we enabled marketing emails. These were built with a drag-and-drop email builder and used nested tables for everything. The hero section was a table with a background image. The two-column content grid was two tables side by side. The footer was a table with three cells.

@native-html/render rendered the content but the layout collapsed. Columns stacked vertically. Background images disappeared. The emails looked broken.

We switched to WebView for marketing emails and kept @native-html/render for transactional emails. The WebView rendered everything correctly—tables, background images, conditional comments. The tradeoff was a slight delay when opening marketing emails (the WebView had to initialize) and a less native feel when scrolling.

The two-renderer approach worked well. We used a simple heuristic: if the email HTML contains more than three <table> tags, use WebView. Otherwise, use the native renderer. This kept most emails fast and native, while ensuring complex ones rendered correctly.

The lesson: there's no single perfect solution for rendering email HTML in React Native. The right approach depends on the email content. Be prepared to use multiple strategies.

Wrapping Up

Rendering HTML email in React Native is a challenge because email HTML is built for a different world—one with tables, inline styles, and browser inconsistencies. React Native's native rendering model doesn't map cleanly to that world.

The three approaches each have a place. WebView gives you maximum fidelity at the cost of performance and native feel. Native renderers like @native-html/render give you performance and native feel at the cost of fidelity. Server-side pre-processing gives you control at the cost of upfront work.

For email clients specifically, WebView is often the pragmatic default for complex marketing emails, with native rendering for simpler transactional ones. For apps that just need to display HTML content from a CMS, @native-html/render is the better choice.

If you're building the sending side and generating HTML emails with React, see our React Email component guide. And if you're working with real-time data streaming in React Native, our WebSocket vs SSE comparison covers the protocol choices.

The good news: the tools have gotten significantly better in the last few years. @native-html/render is actively maintained by Software Mansion, WebView is stable and well-documented, and the React Native community has built solutions for most of the pain points. It's still not easy, but it's no longer a research project.


Need help building email features or HTML rendering in your React Native app? Red Surge Technology specializes in mobile development and content-rich interfaces. Get in touch to discuss your project.