dangerouslySetInnerHTML injects a raw HTML string into the DOM without React's normal escaping. If any part of that string comes from a user, an attacker can include a script that runs in your other users' browsers with their session. The fix is to render text normally, or to sanitize the HTML before you inject it.
Why it's a problem
React escapes values by default, which is what normally protects you from cross-site scripting. dangerouslySetInnerHTML turns that off for a chunk of markup. Feed it user-controlled content, and a payload like an image tag with an onerror handler executes in the victim's browser, letting the attacker steal session tokens, act as the user, or rewrite the page.
The pattern
// user-controlled content injected as raw HTML
<div dangerouslySetInnerHTML={{ __html: comment.body }} />The fix
// render as text (React escapes it)
<div>{comment.body}</div>
// or, if you truly need HTML, sanitize first
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment.body) }} />Why AI tools write this
When the task is 'render this rich text' or 'show the HTML from the API,' dangerouslySetInnerHTML is the completion that makes it appear on screen immediately. The assistant produces working output without weighing whether the content is trusted, because trust is a fact about your data it cannot see.
The quick fix
- Render user content as text with {value} so React escapes it.
- If you must inject HTML, sanitize it first with a library like DOMPurify.
- Never pass unsanitized user input to dangerouslySetInnerHTML.
Common questions
Can I use dangerouslySetInnerHTML safely with content from my own database?
If you fully control the content and it was never user-generated or passed through untrusted input, the risk is lower but still non-zero. For safety, sanitize with DOMPurify before injecting even your own stored HTML.
Does DOMPurify work in Next.js server components?
DOMPurify runs in the browser and requires a DOM. In a server component, use a library like sanitize-html that runs in Node.js without a DOM.
If I render text as {value} instead of dangerouslySetInnerHTML, am I protected?
Yes. React's JSX escapes all interpolated values by default, which is the safest option for plain text content.