iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

DOMPurify

DOMPurify is the de-facto JavaScript library for sanitising HTML against XSS. When you must accept rich user HTML (rich-text editors, markdown, oEmbed), pass it through DOMPurify before assigning to innerHTML.

Sanitise on the client, configure tags, hooks

EXAMPLE
import DOMPurify from 'dompurify';

// 1) Defaults — already very strict
const dirty = `<img src=x onerror=alert(1)><p>hello</p>`;
const clean = DOMPurify.sanitize(dirty);
// → "<img src=\"x\"><p>hello</p>"  — the onerror is gone

element.innerHTML = clean;     // safe to assign now

// 2) Allow a specific tag list (e.g. lightweight markdown output)
const safe = DOMPurify.sanitize(html, {
    ALLOWED_TAGS:  ['a', 'b', 'i', 'em', 'strong', 'p', 'br', 'ul', 'ol', 'li', 'code', 'pre'],
    ALLOWED_ATTR:  ['href', 'title'],
    ALLOW_DATA_ATTR: false,
});

// 3) Force every link to open in a new tab + lose referrer
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
    if (node.tagName === 'A' && node.hasAttribute('href')) {
        node.setAttribute('target', '_blank');
        node.setAttribute('rel',    'noopener noreferrer');
    }
});

const safer = DOMPurify.sanitize(html);

// 4) Strip dangerous URI schemes — block javascript:, data: (mostly)
DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {
    if (data.attrName === 'href' || data.attrName === 'src') {
        if (/^\s*(javascript|data|vbscript):/i.test(data.attrValue)) {
            data.keepAttr = false;
        }
    }
});

// 5) Reject anything BUT a known set of tags / attrs (deny-by-default)
const veryStrict = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['p', 'br', 'strong', 'em'],
    ALLOWED_ATTR: [],
});

// 6) Server-side — JSDOM + DOMPurify (Node)
// npm i isomorphic-dompurify jsdom
import DP from 'isomorphic-dompurify';
const safeOnServer = DP.sanitize(userHtml);

// 7) Common framework integrations
// React — assign through dangerouslySetInnerHTML
function RichBody({ html }) {
    const clean = useMemo(() => DOMPurify.sanitize(html), [html]);
    return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

// Vue — v-html (still call DOMPurify first)
<div v-html="sanitisedBody"></div>

// 8) DON'T do these
// element.innerHTML = userHtml                       // raw injection
// element.innerHTML = userHtml.replace('<', '&lt;')  // half-baked, never enough

Why it matters

Sanitise once, at the boundary — either when storing OR when rendering, not both. Most teams sanitise on render (so the raw HTML stays intact for future re-sanitising as DOMPurify improves).

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Browser
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userHtml);
// Node (with jsdom)
import createDOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';
const DOMPurify = createDOMPurify(new JSDOM('').window);
Try it Yourself »

Test yourself

Q1. DOMPurify is for…
Q2. It should be used when…
Q3. Prefer running DOMPurify…

Discussion

Loading…