XSS in React Applications: A Safe Data Handling Guide

XSS in React projects: where JSX escaping protects you and where it does not, dangerouslySetInnerHTML and DOMPurify, URL-based vectors, CSP and the token storage decision.

Editor window showing safe HTML handling with DOMPurify in a React component

Did React solve XSS?

Mostly — but not entirely. JSX automatically escapes every value bound with curly braces, which closes most classic XSS vectors. But React deliberately leaves some doors outside its protection, and XSS findings in modern React projects almost always come through those doors. This article maps them.

Where does XSS enter a React app?

Vector

Why it works

Control

dangerouslySetInnerHTML

Deliberately disables escaping

Sanitize with DOMPurify; avoid entirely where possible

javascript: URLs

User data bound to href/src can execute code

URL scheme validation (http/https allowlist)

Third-party scripts

Every added script runs with full authority

Minimal third parties; CSP; integrity (SRI)

npm dependencies

A hijacked package becomes your application code

Lockfiles, scanning, version discipline

Data leaking into server-side templates

Raw HTML generation during SSR/hydration

Escape in SSR output too; template discipline

dangerouslySetInnerHTML: the name is the warning

When you must render raw HTML — CMS content, rich-text output, email templates — the only safe path is sanitizing it with a proven library such as DOMPurify before rendering. And sanitize on the server, not only in the client: if your API stores raw HTML unsanitized, every new consumer of that data (a mobile app, another frontend) reproduces the same risk.

URLs: href is an execution surface too

Rendering a user-supplied link as <a href={url}> can turn into code execution with a javascript:-scheme value. React warns on javascript: URLs, but the responsibility is the application's: validate every externally sourced URL against an allowlist of schemes (http, https, mailto), and refuse to render anything that fails.

Supply chain: XSS now arrives via npm as well

The dependency tree is one of the frontend's largest attack surfaces, and the risk is not theoretical: malicious code slipped into event-stream in 2018 targeted a specific crypto wallet; ua-parser-js was hijacked in 2021 and malicious versions were published; the 2022 colors.js/faker.js incident showed a single maintainer can break thousands of projects. The control set: lockfiles under version control, dependency vulnerability scanning in CI, human-approved automatic update bots, and restricted install scripts.

CSP: the second line of defense

Content-Security-Policy is the layer that can stop an injected script from running or exfiltrating data even when everything else fails. The practical path for SPAs: remove the need for inline scripts, restrict script sources to your own domains, and run the policy in Report-Only mode first, tightening it with violation reports.

Where should tokens live: localStorage or cookies?

This decision is directly tied to XSS: a token in localStorage is readable by any script running on the page — one XSS means total session theft. A token in an httpOnly cookie cannot be read from JavaScript; it limits XSS impact to requests within that session, in exchange for requiring CSRF protection (SameSite + tokens). For high-risk applications the balanced choice is httpOnly + Secure + SameSite cookies with short-lived access tokens.

Business impact: frontend trust is measurable

XSS is the main door to session theft and in-page card skimming (web skimming); in e-commerce it has a regulatory price tag too — the UK regulator's £20 million fine against British Airways followed a web skimming incident. Enterprise security questionnaires now routinely ask about frontend dependency policy and CSP; the vendor with ready answers clears review faster.

Frequently asked questions

Do I need sanitization if I use JSX?

Not for plain values rendered with curly braces — React escapes them. If you render raw HTML (dangerouslySetInnerHTML), yes, always.

Is DOMPurify enough?

It is the industry standard for HTML sanitization and strong when configured correctly; but it does not replace the other layers — URL validation, CSP and dependency discipline.

Does SSR (Next.js etc.) change the risk?

The fundamentals are the same; additionally, watch escaping discipline in server-generated HTML and hydration data — especially patterns that embed raw JSON inside script tags.

Are inline event handlers (onClick) risky?

Handlers bound as function references in JSX are safe; the risk lies in handlers embedded as strings into HTML and in raw HTML rendering.

Frontend security checklist

  1. Inventory of dangerouslySetInnerHTML usage; every instance passes through DOMPurify

  2. Scheme allowlist enforced on externally sourced URLs

  3. Lockfile under version control; dependency scanning active in CI

  4. Third-party script inventory minimal; SRI where possible

  5. CSP defined (Report-Only first, then enforced)

  6. Token storage decision documented (httpOnly cookies + CSRF controls preferred)

  7. Source maps and debug output disabled in production builds

SSH Yazılım offers frontend security audits, CSP rollout and dependency security process design for React-based applications. Get in touch to benchmark your frontend.