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.
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
Inventory of dangerouslySetInnerHTML usage; every instance passes through DOMPurify
Scheme allowlist enforced on externally sourced URLs
Lockfile under version control; dependency scanning active in CI
Third-party script inventory minimal; SRI where possible
CSP defined (Report-Only first, then enforced)
Token storage decision documented (httpOnly cookies + CSRF controls preferred)
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.