Is React vulnerable to XSS?
React was designed with XSS prevention in mind, but is it completely safe from XSS attacks? Learn how your React applications are protected against cross-site scripting, when they can still be vulnerable, and what best practices (and testing methods) will help to keep your application environments secure.
Your Information will be kept private.
Begin your DAST-first AppSec journey today.
Request a demo
React is designed to help developers build secure and dynamic web applications. However, like any front-end framework, it can still be vulnerable to cross-site scripting (XSS) if used incorrectly. Understanding React’s built-in protections and the scenarios in which they can fail is essential for writing secure components.
Understanding XSS and its impact on web applications
What is cross-site scripting (XSS)?
XSS is a security vulnerability that allows attackers to inject malicious scripts into web pages viewed by other users. These scripts run in the victim’s browser and can steal sensitive data, impersonate users, or manipulate application behavior. It remains one of the most common vulnerabilities in web applications, frequently appearing in the OWASP Top 10.
Why XSS is a common threat in modern front-end apps
Modern front-end frameworks rely heavily on client-side rendering, dynamic content, and user interactions, increasing the likelihood of developers inadvertently introducing unsafe handling of user input. Without proper validation and sanitization, user-controlled data can be executed in the browser context, leading to XSS.
Types of XSS: Reflected, stored, and DOM-based
- Reflected XSS occurs when malicious input is immediately echoed back to the browser in the HTTP response, such as via URL parameters.
- Stored XSS involves persisting malicious scripts in the backend (e.g., in a database), which are later delivered to users through normal application flows.
- DOM-based XSS exploits vulnerabilities in client-side scripts that dynamically modify the DOM based on user input without sanitization.
How React helps prevent XSS by design
The React framework (initially React.js) was one of the first to be built with security in mind and includes features that make it harder to introduce XSS vulnerabilities. One of the core innovations in React is JSX, a syntax extension that allows developers to write HTML-like elements directly within JavaScript. JSX is compiled to JavaScript and rendered using React’s virtual DOM, which adds a layer of abstraction and safety when interacting with the DOM.
Automatic escaping in JSX
React helps protect against XSS primarily by escaping values rendered in JSX before inserting them into the DOM.
How React renders HTML safely
React escapes all values embedded in JSX expressions by default before rendering them to the DOM. This is done using a mechanism that ensures injected data is treated as plain text, not interpreted as executable HTML or JavaScript. It significantly reduces the likelihood of XSS through normal data binding.
What characters get escaped and why
Characters like <
, >
, &
, '
, and "
are escaped to their respective HTML entities (e.g., <
, >
). This prevents them from being parsed as HTML tags or attributes, which could otherwise enable code injection.
Virtual DOM and safe rendering
React’s virtual DOM adds another layer of protection by controlling how updates are applied to the real DOM. Instead of directly manipulating the DOM based on strings or templates, React builds a virtual representation that is diffed and sanitized before updates are made. This minimizes the risk of unexpected script execution.
Handling dynamic content in React
Dynamic content from user input or APIs can be safely displayed using JSX, as React escapes it by default. However, if developers bypass these mechanisms—such as by injecting HTML manually—XSS risks reappear. Safe rendering requires avoiding raw HTML and relying on trusted sanitization libraries when necessary.
What can make React apps vulnerable to XSS?
Despite its protective defaults, React is not immune to XSS vulnerabilities. Developers can still introduce risk through specific APIs or by misusing features that bypass the normal safety mechanisms. JSX itself is safe when used properly, but it is not a sandbox—it will render whatever it’s instructed to, which still makes secure coding practices essential.
Using dangerouslySetInnerHTML
One of the most common ways XSS can re-enter a React app is through the use of dangerouslySetInnerHTML
, which bypasses React’s built-in escaping.
Why it exists
The dangerouslySetInnerHTML
attribute exists to support scenarios where raw HTML needs to be rendered, such as displaying content from a CMS or rendering HTML-formatted Markdown. Its usage is explicitly marked as “dangerous” to discourage misuse and highlight the associated security risks.
How it can be abused
If developers use dangerouslySetInnerHTML
to render content that includes user input or untrusted data, attackers can inject scripts that will be executed in the user’s browser. This bypasses React’s escaping mechanisms entirely and creates a direct path for XSS.
Improper use of eval()
, innerHTML
, or document.write()
Functions like eval()
or properties like innerHTML
are inherently dangerous because they can execute arbitrary JavaScript. Using them on untrusted data creates XSS vulnerabilities, as any malicious script passed in will run with full privileges in the page context.
Exposing React apps to untrusted third-party scripts
Loading external scripts into a React application without integrity checks or sandboxing introduces risks. These scripts could be compromised or behave maliciously, potentially accessing sensitive data or manipulating the app’s state.
Client-side routing issues with unescaped inputs
Client-side routing libraries often use dynamic segments from URLs to render content. If those segments are injected directly into the DOM without escaping or sanitization, attackers can exploit them to execute scripts via DOM-based XSS.
Real-world examples of XSS in React apps
Example 1: Using unsanitized Markdown
Consider a component that renders Markdown using a parser that outputs raw HTML. If this output is injected with dangerouslySetInnerHTML
without sanitization, attackers can exploit it.
function MarkdownViewer({ content }) {
return (
<div dangerouslySetInnerHTML={{ __html: marked(content) }} />
);
}
// Malicious input: `')"`
Without sanitizing content
, this approach can allow injection of <img>
tags with onerror
handlers that execute JavaScript.
Example 2: External data injection via APIs
Suppose an app fetches a news summary from an external API and renders it with raw HTML.
useEffect(() => {
fetch('/api/news')
.then(res => res.text())
.then(html => setContent(html));
}, []);
return <div dangerouslySetInnerHTML={{ __html: content }} />;
If the API is compromised or the data is not properly filtered, attackers can inject scripts through the returned HTML, exposing users to XSS.
Example 3: Misconfigured dangerouslySetInnerHTML
Even with good intentions, misconfigured or misunderstood uses of dangerouslySetInnerHTML
can introduce XSS.
const SafeHtml = ({ rawHtml }) => (
<div dangerouslySetInnerHTML={{ __html: rawHtml }} />
);
// rawHtml might contain: `<script>alert('XSS')</script>`
Unless rawHtml
is sanitized before being passed in, this will execute arbitrary scripts embedded in the input.
Best practices to prevent XSS in React
Don’t use dangerouslySetInnerHTML
unless absolutely necessary
Avoid it whenever possible. When raw HTML must be rendered, sanitize the content using a robust library and validate the source of the data.
Use trusted libraries to sanitize HTML
Libraries like DOMPurify are specifically designed to clean HTML and remove potentially dangerous elements and attributes. Always use them before injecting HTML into the DOM.
Validate and sanitize inputs on the backend
Server-side validation ensures that malicious data does not enter your system, complementing client-side protections. It also prevents tampered requests or API responses from introducing harmful content.
Avoid inline JavaScript and inline event handlers
React’s design discourages inline handlers for good reason. Stick to JSX event binding and avoid constructing handlers from user input, which could result in code injection.
Set a strict Content Security Policy (CSP)
A strong CSP can block script execution from unauthorized sources and add a critical layer of defense. It’s especially useful as a fallback when other protections fail.
Using security headers with React
As with any other web application, your React-based app should use best-practice security headers to minimize the risk not only of XSS but of many other classes of attacks.
Implementing Content Security Policy (CSP)
Add CSP headers to your HTTP responses to control script execution. For example, setting Content-Security-Policy: default-src 'self'
restricts scripts to those hosted on your own domain.
HTTPOnly and Secure cookies
Mark authentication cookies as HttpOnly
to prevent access from JavaScript and as Secure to ensure they are transmitted only over HTTPS. This protects session data even if other parts of the app are compromised.
Referrer-Policy and X-XSS-Protection headers
Set Referrer-Policy
to limit sensitive referrer data in requests. While X-XSS-Protection
is deprecated, it may still offer limited protection in older browsers.
Testing React apps for XSS vulnerabilities
Static code analysis tools
Linting tools and static analyzers can identify common coding issues, including unsafe API usage. However, they lack execution context and often miss runtime vulnerabilities.
Manual pen testing techniques
Security professionals can simulate attack scenarios to test how the app handles malicious input. This includes checking for DOM manipulation flaws and unsafe data handling patterns.
Using DAST tools (vulnerability scanners)
Dynamic application security testing (DAST) tools like Invicti interact with live applications to identify real, exploitable XSS vulnerabilities. With capabilities like proof-based scanning, these tools provide verified results that help teams focus on fixing true security issues rather than chasing false positives.
Learn more about cross-site scripting in React applications
Frequently asked questions (FAQ)
Is React immune to XSS?
No. While React reduces the risk with built-in protections like automatic escaping, XSS can still occur if developers introduce insecure code.
Why does React have dangerouslySetInnerHTML?
It allows rendering raw HTML when necessary. However, it should be used with extreme caution and only with trusted, sanitized content.
What is the safest way to render user-generated content in React?
Use a library like DOMPurify to sanitize HTML before rendering it, and avoid dangerouslySetInnerHTML unless absolutely necessary.
Does React prevent DOM-based XSS?
React mitigates many DOM-based XSS vectors by not allowing direct DOM manipulation through JSX. However, careless coding practices can still introduce risk.
Can Content Security Policy (CSP) help secure a React app?
Yes. A strong CSP adds an additional layer of defense by restricting where scripts can be loaded from, preventing inline execution of malicious code.