Safeguard
Developer Tools•

How do you sanitize HTML in a React app without undoing it?

Sanitize at the last step, restrict the allowed profile, keep the library current and do not edit the HTML afterwards. A practical sanitizing routine.

Safeguard Security Team
4 min read

Run untrusted HTML through a maintained sanitizer immediately before it reaches the DOM, allow only the markup you need, and do not change the result afterwards. In React that usually means sanitizing and then passing the clean string to dangerouslySetInnerHTML. Keep the sanitizer updated.

When you need this

Most pages should render user text as plain text, which needs no sanitizer. You need one when the product genuinely shows user or third party HTML: rich text comments, rendered markdown, CMS content, email previews. In those cases OWASP recommends an allowlist sanitizer and says to enforce Trusted Types with no permissive default policy.

A routine that holds up

  1. Pick a maintained sanitizer. DOMPurify is a widely used example. Its README describes it as a DOM-only XSS sanitizer for HTML, MathML and SVG.
  2. Sanitize as late as possible, in the component or a small helper right before rendering, so the sanitized string cannot be altered along the way.
  3. Narrow what it allows. The README notes that by default it permits HTML, SVG and MathML, and shows how to restrict to HTML only with a profile setting.
  4. Do not edit the output. The README warns that if you first sanitize HTML and then modify it afterwards, you might easily void the effects of sanitization, and that the same applies if you pass the clean markup to another library that changes it.
  5. Wrap the call in a single helper used everywhere, so a lint rule can ban direct use of dangerouslySetInnerHTML elsewhere.
import DOMPurify from 'dompurify'

export function cleanHtml(dirty) {
  return DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } })
}

Server side rendering needs extra care

If you sanitize on the server, the sanitizer needs a DOM implementation. The DOMPurify README says sanitization can end in XSS even when the sanitizer does everything correctly if the server side DOM has flaws, and it advises keeping that DOM library up to date, citing known attack vectors fixed in a later jsdom version. The DOM library you choose becomes part of your trusted computing base, so put it in your dependency review.

What a sanitizer does not do

QuestionSanitizer answer
Does it validate links you set in JSX yourself?No, it cleans the markup you pass in, so validate your own URL props separately
Does it make the source trustworthy?No, it reduces risk from markup, not from content
Is it set and forget?No, update it, since attackers keep finding parser tricks
Does it replace CSP?No, use both

Track the sanitizer like any dependency

Because a sanitizer is a security control, a vulnerable old version is a real exposure. Put it on a short update cycle, watch advisories and check it appears in your inventory and not only in a lockfile of one app.

Try it yourself

Click through it at your own pace, or open it full screen. Look at how packages and versions are listed per project, so you can confirm which apps use which sanitizer version.

Safeguard's dependency scanning and the free scan at gold.safeguard.sh/scan can show the sanitizer version in each project.

Quick check

Can I sanitize once when saving and skip it on render?

Sanitizer rules and attack knowledge change over time, so sanitizing on output, right before use, is the safer habit.

Is DOMPurify required?

No. Any maintained allowlist sanitizer works. The point is to use one, restrict it and avoid editing its output.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.