Skip to main content
Developer Tools Reference

HTML Email Rendering Guide & Playground

Discover the 5 common ways HTML emails are rendered on websites. Play with style leakage check, sandboxing limits, and analyze the raw code to implement them in your SaaS products.

Interactive Rendering Sandbox

Edit the email HTML below and toggle paradigms to inspect isolation shields, script execution security, and scanner scores.

HTML Email Editor
Rendering Paradigm Preview
Style BleedFully Isolated
JS Script executionBlocked (Secure)
Email Score255 / 250
emailens-detection-simulator.log

The 5 Rendering Paradigms: Implementation & Detection

1

Direct Light DOM Injection

The email HTML is injected directly into a standard container element (like a div) using dynamic markup insertion. This is the simplest strategy but carries substantial risks: email styles leak outwards and contaminate the parent application layout, while global dashboard CSS rules bleed inwards and corrupt the email preview.

Implementation Code
LightDOMRenderer.tsx
// Paradigm 1: React Light DOM Renderer
import React from 'react';

export function LightDOMRenderer({ emailHtml }: { emailHtml: string }) {
  return (
    <div 
      className="email-light-dom-container"
      dangerouslySetInnerHTML={{ __html: emailHtml }} 
    />
  );
}
How emailens Detects It
detect.ts
// Chrome Extension Scanner Fallback
// Scans the top-level body document (weighted with a penalty since it's not isolated)
const html = document.documentElement.outerHTML;
const score = calculateScore(html) - 50;
2

Shadow DOM Encapsulation

The email is loaded inside a browser-isolated Shadow DOM root attached to a host container. This completely solves the style bleeding issue without spinning up separate heavy iframe window threads. However, script blocks can still run unless intercepted, and default web tools like document.querySelectorAll will not see the email markup unless they explicitly drill through open shadowRoot nodes.

Implementation Code
ShadowDOMRenderer.tsx
// Paradigm 2: React Shadow DOM Renderer (Encapsulated)
import React, { useEffect, useRef } from 'react';

export function ShadowDOMRenderer({ emailHtml }: { emailHtml: string }) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!containerRef.current) return;
    
    // Attaching or recycling open shadow root
    let shadow = containerRef.current.shadowRoot;
    if (!shadow) {
      shadow = containerRef.current.attachShadow({ mode: 'open' });
    }
    shadow.innerHTML = `
      <style>
        :host { display: block; width: 100%; min-height: 500px; }
      </style>
      ${emailHtml}
    `;
  }, [emailHtml]);

  return <div ref={containerRef} className="w-full" />;
}
How emailens Detects It
detect.ts
// Chrome Extension Shadow Root Scanner
function deepSearchShadowRoots(root: ParentNode | ShadowRoot): string[] {
  const results: string[] = [];
  const elements = root.querySelectorAll("*");
  
  for (const el of Array.from(elements)) {
    if (el.shadowRoot) {
      results.push(el.shadowRoot.innerHTML);
      results.push(...deepSearchShadowRoots(el.shadowRoot));
    }
  }
  return results;
}
3

Static CORS Iframe Preview

An iframe with a static src attribute loading from an external rendering API or storage URL. This provides maximum security and isolation since the third-party domain operates outside the main site origin. The downside is that browser security restricts content scripts from accessing the iframe's DOM directly. To solve this, the emailens background engine intercepts the source URI, performs a SameSite-credentialed proxy request, and extracts the payload.

Implementation Code
CORSPreview.html
<!-- Paradigm 3: Cross-Origin Iframe Template Preview -->
<iframe 
  src="https://preview.klaviyo.com/email/render/template_12345" 
  width="100%" 
  height="600px" 
  sandbox="allow-popups allow-same-origin"
  className="border-none"
></iframe>
How emailens Detects It
detect.ts
// Chrome Extension CORS Bypass Scanner
// Identifies CORS-blocked iframes and sends to background context for credentials injection
try {
  const innerDoc = iframe.contentDocument || iframe.contentWindow?.document;
  if (!innerDoc) throw new Error("Blocked by CORS");
} catch {
  // Direct access denied! Delegate bypass to extension runtime proxy
  chrome.runtime.sendMessage({ 
    type: "FETCH_CROSS_ORIGIN", 
    url: iframe.src 
  }, (response) => {
    const rawEmailHtml = response.html; // Inherits cookie session of current tab!
  });
}
4

Dynamic Document Stream Writer

An empty same-origin blank iframe is mounted in the viewport, and JavaScript synchronously writes the raw email HTML directly to its open document stream. It solves style bleeding and is fully readable by content scripts immediately. Developers must be careful to call doc.close() to finalize parsing and stop loading animations.

Implementation Code
DynamicIframe.tsx
// Paradigm 4: React Dynamic Stream Iframe Writer
import React, { useEffect, useRef } from 'react';

export function DynamicIframeRenderer({ emailHtml }: { emailHtml: string }) {
  const iframeRef = useRef<HTMLIFrameElement>(null);

  useEffect(() => {
    const iframe = iframeRef.current;
    if (!iframe) return;

    const doc = iframe.contentDocument || iframe.contentWindow?.document;
    if (!doc) return;

    // Direct synchronous document stream injection
    doc.open();
    doc.write(emailHtml);
    doc.close();
  }, [emailHtml]);

  return (
    <iframe
      ref={iframeRef}
      width="100%"
      height="600px"
      className="border-none"
      title="Dynamic Preview"
    />
  );
}
How emailens Detects It
detect.ts
// Chrome Extension Same-Origin Iframe Scanner
const doc = iframe.contentDocument || iframe.contentWindow?.document;
if (doc && doc.documentElement) {
  const emailHtml = doc.documentElement.outerHTML;
  const score = calculateScore(emailHtml, iframe);
}
5

The srcdoc Attribute Iframe (Highly Recommended)

The modern standard. Email HTML is embedded inside the srcdoc attribute of an iframe. When paired with standard sandboxing parameters (e.g. sandbox="allow-same-origin"), this achieves perfect style encapsulation, blocks script execution, and loads completely offline. The emailens engine extracts the raw unescaped content directly from the iframe's DOM attribute, bypassing loading delays.

Implementation Code
SrcdocIframe.html
<!-- Paradigm 5: The Secure Offline srcdoc Iframe -->
<iframe 
  srcdoc="<!DOCTYPE html><html><body><h1>Safe Preview</h1></body></html>" 
  width="100%" 
  height="600px" 
  sandbox="allow-popups allow-same-origin"
  className="border-none rounded-xl"
></iframe>
How emailens Detects It
detect.ts
// Chrome Extension Direct srcdoc Extractor (SPA Lag Bypass)
const srcdoc = iframe.getAttribute("srcdoc");
if (srcdoc && srcdoc.trim() !== "") {
  // Instantly read raw escaped HTML without waiting for iframe DOM rendering threads
  const emailHtml = srcdoc; 
}

Debug Your Email Layouts Instantly

Don't let style bleeds and parsing limits ruin your email previews. Use the Emailens Chrome Extension or local CLI to audit email templates against 250+ email clients in real time.