HTML Sanitizer API
The HTML Sanitizer API sanitizes untrusted HTML before DOM insertion, removing XSS-dangerous content (scripts, event handlers) using the browser's own parser โ eliminating parser-differential vulnerabilities that plague userland sanitizers.
Browser support: Limited availability โ the API is under active development and has undergone significant redesign. Support for setHTMLUnsafe() and the redesigned setHTML() with sanitizer config varies across browsers and is evolving rapidly; check the MDN browser compatibility table for current status. The Sanitizer configuration API is still evolving. Per our requirement engineering guidance, use DOMPurify in production today and adopt the native API once it stabilizes and reaches Baseline.
Use casesโ
- Rich-text rendering (CMS content, user-generated HTML)
- Email HTML display
- Markdown-to-HTML output sanitization
- Clipboard paste handling (
pasteevent with HTML payloads) - Comment systems with limited formatting
Example โ basic setHTML()โ
setHTML() parses, sanitizes, and inserts HTML in one atomic step โ no intermediate string that could be accidentally used unsanitized:
const dirty = `<p>Hello</p><script>alert("xss")</script><img onerror="steal()" src=x>`;
// Default sanitizer strips scripts and event handlers
document.getElementById("output").setHTML(dirty);
// Result: <p>Hello</p><img src="x">
Example โ custom configurationโ
The Sanitizer constructor accepts options to tighten or loosen the defaults:
const sanitizer = new Sanitizer({
elements: ["p", "b", "i", "a", "ul", "li"],
attributes: {
href: ["a"],
},
removeElements: ["script", "style"],
comments: false,
});
const dirty = `<a href="/safe" onclick="evil()"><b>Click</b></a><custom-el>hi</custom-el>`;
document.getElementById("output").setHTML(dirty, { sanitizer });
// Result: <a href="/safe"><b>Click</b></a>
Key configuration options:
elements/replaceWithChildrenElements/removeElementsโ control which elements survive, are unwrapped (keep children), or are fully removedattributes/removeAttributesโ control attributes per elementcommentsโ whether HTML comments are kept (default:false)dataAttributesโ whetherdata-*attributes are kept (default:true)
Example โ feature detection with DOMPurify fallbackโ
import DOMPurify from "dompurify";
function setSanitizedHTML(element, dirty) {
if (element.setHTML) {
// Native: atomic parse + sanitize + insert โ no parser differentials
element.setHTML(dirty);
} else {
// Fallback: DOMPurify returns a sanitized string
element.innerHTML = DOMPurify.sanitize(dirty);
}
}
Comparison with DOMPurifyโ
| Feature | HTML Sanitizer API | DOMPurify |
|---|---|---|
| Bundle size | 0 kB (built-in) | ~15 kB minified |
| Browser support | Experimental (Chrome 146+) | All modern browsers |
| Output type | DOM-only (setHTML()) | String or DOM |
| Hooks / callbacks | None | Yes (extensive) |
| Server-side (Node.js) | No | Yes (with jsdom) |
| Production readiness | Not yet | Yes โ battle-tested |
| Parser-differential risk | None (uses browser parser) | Minimal but nonzero |
When to useโ
Use DOMPurify today for production. Add feature detection for setHTML() to benefit from atomic parse+sanitize+insert when available โ eliminating the class of bugs where a sanitized string is re-parsed differently. Plan full migration once the API reaches Baseline.
Limitationsโ
- DOM-only output โ no method to get a sanitized string; you must insert directly via
setHTML() - No hooks or callbacks โ cannot modify nodes mid-sanitization (DOMPurify's
afterSanitizeAttributeshas no equivalent) - No server-side usage โ browser API only, not available in Node.js
- No easy React / virtual DOM integration โ requires a ref to a real DOM node
- Spec still evolving โ the API surface has changed multiple times; expect further changes before stabilization