dangerouslySetInnerHTML in React

`dangerouslySetInnerHTML` lets a React component insert an HTML string directly into the DOM instead of rendering it through JSX's normal escaping. Because the HTML is trusted by React rather than escaped, the value must be treated as a security-sensitive boundary.

Normally, JSX escapes text inserted into an element, so a string such as `<img src=x>` is displayed as text rather than interpreted as markup. `dangerouslySetInnerHTML` intentionally bypasses that behavior and supplies HTML to the element's `innerHTML`-style rendering path.

The API takes an object with an `__html` property, for example ``. React uses the supplied string as markup, so the caller is responsible for ensuring that it is safe for the intended context.

The main security concern is cross-site scripting (XSS). If attacker-controlled HTML reaches this API without appropriate sanitization, injected markup can potentially execute scripts or trigger other unwanted browser behavior. Sanitization should therefore happen before the value crosses this boundary, using a well-maintained HTML sanitizer appropriate to the application's threat model.

A senior engineer should also consider trust boundaries and data flow rather than merely searching for the API call. Content from a database is not automatically safe, and encoding data at input time is not equivalent to safely sanitizing HTML when HTML is intentionally allowed. Prefer ordinary JSX for structured UI whenever possible and reserve raw HTML rendering for cases that genuinely require it.

Key points

Interview questions

Why is `dangerouslySetInnerHTML` considered dangerous?

Because React normally escapes strings rendered through JSX, which prevents a string containing HTML from being interpreted as markup. `dangerouslySetInnerHTML` deliberately disables that protection for the supplied HTML. If an attacker can influence the value, malicious markup may reach the browser. The application therefore needs an explicit trust or sanitization strategy.

How would you safely render user-generated rich text?

I would first define which HTML elements and attributes the product actually needs. I would sanitize the generated HTML with a maintained sanitizer configured with an allowlist appropriate to that requirement, then pass only the sanitized result to `dangerouslySetInnerHTML`. I would also inspect the full data flow because safety depends on where the content originates and whether it can be modified later. If rich HTML is unnecessary, I would keep the content as text and render it with ordinary JSX.

Does storing HTML in a database make it safe to render later?

No. Persistence does not establish trust. Data may have originated from users, integrations, administrators, imports, or previously compromised systems, and its trust properties need to be established independently. If the application intentionally supports HTML, the value should be validated or sanitized according to the rendering context before it reaches `dangerouslySetInnerHTML`.

What is the difference between escaping and sanitizing?

Escaping changes characters so that data is treated as text rather than markup in a particular output context. Sanitization instead parses or analyzes content and removes or neutralizes constructs that are not permitted while preserving an allowed subset of markup. For plain text, contextual escaping is usually the appropriate defense. For intentionally supported rich HTML, a suitable sanitizer is generally needed before raw HTML rendering.

What should you investigate during a security review of `dangerouslySetInnerHTML` usage?

I would trace every source feeding the `__html` value and identify which sources are attacker-influenced. Then I would determine whether a recognized sanitizer is applied, what its policy allows, and whether that policy matches the application's required HTML features. I would also review adjacent URL, attribute, and script-related handling because a sanitizer configuration can be unsafe if it permits dangerous constructs. Finally, I would look for simpler JSX-based alternatives and add tests around the security boundary.