How to Fix Cross-Site Scripting (XSS) in React
Learn how to prevent and fix Cross-Site Scripting (XSS) vulnerabilities in React applications. Step-by-step guide with code examples, security checklists, and best practices.
What Is Cross-Site Scripting (XSS)?
Cross-Site Scripting (XSS) is a code injection vulnerability that occurs when an application includes untrusted data in a web page without proper validation or escaping. An attacker can inject malicious scripts (typically JavaScript) that execute in the context of a victim's browser session.
There are three main types of XSS: Reflected XSS, where the malicious script comes from the current HTTP request; Stored XSS, where the script is permanently stored on the target server (e.g., in a database or comment field); and DOM-based XSS, where the vulnerability exists entirely in client-side code that processes data from an untrusted source.
Modern frameworks like React and Vue provide automatic output encoding by default, but developers can still introduce XSS through dangerous APIs like `dangerouslySetInnerHTML`, `v-html`, or by constructing HTML strings manually. Server-rendered pages are particularly vulnerable when user input flows into template output without sanitization.
Why It Matters
XSS is one of the most prevalent web vulnerabilities and can have devastating consequences. An attacker exploiting XSS can steal session cookies and authentication tokens, impersonate users and perform actions on their behalf, redirect users to malicious websites, deface web pages, and install keyloggers to capture credentials. Because XSS executes in the trusted context of the vulnerable website, it can bypass same-origin policies and access any data the user can see. In applications handling sensitive data -- financial records, health information, or personal communications -- XSS can lead to massive data breaches and regulatory violations.
How to Fix It in React
The primary defense against XSS is output encoding: escape all untrusted data before inserting it into HTML, JavaScript, CSS, or URL contexts. Use your framework's built-in auto-escaping (React JSX, Vue templates, Angular interpolation) and avoid bypassing it with dangerous APIs. Implement a strict Content Security Policy (CSP) that prevents inline script execution. Validate and sanitize all user input on the server side using allowlists rather than denylists. For rich text, use a proven sanitization library like DOMPurify. Set the HttpOnly flag on session cookies to prevent JavaScript access. Use the X-XSS-Protection header as an additional layer of defense.
React-Specific Advice
- Never use `dangerouslySetInnerHTML` with unsanitized user input. If you must render HTML, sanitize it with DOMPurify first.
- Avoid storing sensitive tokens in localStorage -- use HttpOnly cookies instead. localStorage is accessible to any JavaScript running on the page.
- Validate all user inputs before sending them to your API. Use Zod or Yup for schema validation on the client side.
- Be cautious with `href` attributes in links -- `javascript:` URLs can execute code. Validate URLs against an allowlist of schemes (http, https).
Code Examples
function UserBio({ bio }: { bio: string }) {
// DANGEROUS -- XSS if bio contains <script> or event handlers
return <div dangerouslySetInnerHTML={{ __html: bio }} />;
}import DOMPurify from "dompurify";
function UserBio({ bio }: { bio: string }) {
// Sanitize HTML to strip dangerous tags and attributes
const sanitized = DOMPurify.sanitize(bio, {
ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "p", "br"],
ALLOWED_ATTR: ["href", "target"],
});
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}React Security Checklist for Cross-Site Scripting (XSS)
React Security Best Practices
Never use `dangerouslySetInnerHTML` with unsanitized user input. If you must render HTML, sanitize it with DOMPurify first.
Avoid storing sensitive tokens in localStorage -- use HttpOnly cookies instead. localStorage is accessible to any JavaScript running on the page.
Validate all user inputs before sending them to your API. Use Zod or Yup for schema validation on the client side.
Be cautious with `href` attributes in links -- `javascript:` URLs can execute code. Validate URLs against an allowlist of schemes (http, https).
Avoid passing unsanitized data to component props that are rendered as HTML attributes. This can lead to attribute injection.
Use React's built-in XSS protection by rendering text content through JSX expressions `{variable}` rather than string concatenation.
Implement proper error boundaries to prevent information leakage through error messages in production.
When using `eval()`, `Function()`, or `new Function()` for dynamic code -- do not. Find an alternative that does not execute arbitrary strings as code.
Scan Your React App with SafeVibe
Stop guessing if your React app is vulnerable to Cross-Site Scripting (XSS). Run an automated penetration test in minutes and get actionable results.
Start Free Scan