Skip to main content

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 (paste event 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 removed
  • attributes / removeAttributes โ€” control attributes per element
  • comments โ€” whether HTML comments are kept (default: false)
  • dataAttributes โ€” whether data-* 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โ€‹

FeatureHTML Sanitizer APIDOMPurify
Bundle size0 kB (built-in)~15 kB minified
Browser supportExperimental (Chrome 146+)All modern browsers
Output typeDOM-only (setHTML())String or DOM
Hooks / callbacksNoneYes (extensive)
Server-side (Node.js)NoYes (with jsdom)
Production readinessNot yetYes โ€” battle-tested
Parser-differential riskNone (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 afterSanitizeAttributes has 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