Safeguard
Security Guides•

Is React safe from XSS by default? What it covers and what it skips

React escapes text you render in JSX, but escape hatches, URL values and bad sanitizing still cause XSS. Here is where the default protection ends.

Safeguard Security Team
4 min read

Mostly yes for plain text. React renders values you place in JSX as text, so a string does not turn into markup by accident. It does not protect escape hatches such as dangerouslySetInnerHTML, unchecked URL values, or HTML you sanitize badly. Those gaps cause most React XSS bugs.

What "safe by default" means

Cross-site scripting (XSS) happens when attacker-controlled data reaches the page as code instead of text. Modern frameworks reduce the odds because they steer developers toward templating and auto-escaping. OWASP says this plainly, then adds the catch: problems occur when a framework is used insecurely, and it names React's dangerouslySetInnerHTML without sanitizing the HTML as a typical example.

So the honest answer is that React removes the easy mistakes and leaves the deliberate ones to you.

Where the default protection ends

SurfaceProtected by default?What you must do
Text in JSXYes, rendered as textNothing special
dangerouslySetInnerHTMLNo, by designSanitize with a maintained sanitizer, or avoid it
href and src values from usersPartly, see the next sectionValidate the URL scheme and destination yourself
Direct DOM access through refsNoTreat it like plain JavaScript and avoid innerHTML
Third party componentsDepends on the componentReview how they render HTML

The React reference describes the dangerouslySetInnerHTML prop as something to use with extreme caution, and says that if the HTML is based on user data you risk introducing an XSS vulnerability. The long name is a deliberate warning label.

What about javascript: URLs?

React 16.9 started logging a warning when a javascript: URL appeared in an href, and the team said a future major release would throw an error. OWASP's cheat sheet now states that React 19 blocks javascript: URLs in src and href, but that this does not replace application-specific URL validation. A blocked scheme does not tell you the link goes somewhere safe.

A short review routine

  1. Search the codebase for dangerouslySetInnerHTML and list every hit.
  2. For each hit, trace where the HTML string comes from. If a user, a CMS field, an API response or a markdown renderer can influence it, it needs sanitizing.
  3. Search for places that set href or src from data, and check the scheme and host.
  4. Search for innerHTML and insertAdjacentHTML in code that uses refs.
  5. Add a lint rule so new uses get a human look. The eslint-plugin-react project ships a no-danger rule that flags the dangerous props, and its own docs say you can turn it off where you are certain the content is sanitized HTML.

Try it yourself

Click through it at your own pace, or open it full screen. Look at how a finding points to a specific line, which is how you would triage each dangerouslySetInnerHTML hit.

Safeguard scans repositories for vulnerable dependencies and uses reachability analysis to show which are called; you can try a free scan at gold.safeguard.sh/scan.

Quick check

Does React escape everything I render?

It escapes text rendered through JSX. It does not sanitize HTML passed to dangerouslySetInnerHTML, and it does not vet where a link points.

Is dangerouslySetInnerHTML ever fine?

Yes, when the HTML is trusted or has been sanitized by a maintained library and is not changed afterwards. Treat each use as a decision that needs a reviewer.

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.