XSS in React, Next.js, Vue, and Angular: Where Framework Protection Can Fail

Learn where React, Next.js, Vue, and Angular protect against XSS by default, where escape hatches reintroduce risk, and how to test and remediate safely.

Back to Blog

XSS in React, Next.js, Vue, and Angular: Where Framework Protection Can Fail

Modern JavaScript frameworks make many everyday XSS mistakes harder by escaping interpolated text and encouraging structured rendering. That protection is valuable, but it is not absolute. XSS risk returns when developers deliberately bypass escaping, render raw HTML, trust user-controlled URLs, process untrusted rich text, manipulate the DOM directly, or mix server-rendered and client-rendered data in unsafe ways.

The useful security question is not "Is React safe from XSS?" or "Does Angular sanitize everything?" It is "Where does this framework stop protecting me, and where does my application take responsibility?" That boundary is where reviews should focus.

This guide compares React, Next.js, Vue, and Angular from that perspective. It looks at default protection, escape hatches, CMS and Markdown rendering, URL handling, hydration, Content Security Policy, third-party components, and a practical review process.

What framework defaults do well

Modern template systems normally treat ordinary interpolation as text. A value displayed through a normal binding is escaped rather than parsed as arbitrary HTML. That removes a class of mistakes that were common when applications built markup by concatenating strings.

Frameworks also encourage component boundaries, declarative rendering, and centralized routing. Those patterns can make data flow easier to reason about. The problem appears when developers opt out of the safe path because they need rich text, embedded widgets, legacy code, or third-party content.

The shared risk across all four frameworks

Despite different APIs, the security pattern is similar. Safe text rendering becomes risky when an application converts untrusted data into HTML, script, style, or dangerous URL contexts. Framework choice changes the syntax, not the underlying browser trust model.

  • Keep untrusted content as text whenever possible.
  • Sanitize rich HTML before raw rendering.
  • Validate user-controlled URLs and protocols.
  • Avoid string-to-code execution.
  • Do not trust API data simply because it came from your own backend.
  • Review third-party components that accept HTML strings.
  • Use CSP as defense in depth.
  • Add tests around every approved raw-HTML path.

React XSS risks

React escapes values rendered through normal JSX interpolation. Rendering {userInput} creates text rather than interpreting the value as HTML. The main raw HTML escape hatch is dangerouslySetInnerHTML, which tells React to insert HTML supplied by the application.

function SafeComment({ text }) {
  return <p>{text}</p>;
}

function RichComment({ html }) {
  return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

The second pattern is not automatically vulnerable, but the application must establish why html is safe. If it comes from users, imported content, an API, or a CMS, a deliberate sanitization boundary is usually required.

React patterns that deserve review

  • dangerouslySetInnerHTML with remotely sourced content.
  • Direct DOM manipulation through refs or third-party libraries.
  • Dynamic links built from untrusted values without protocol validation.
  • Markdown or WYSIWYG output converted to HTML without sanitization.
  • Third-party components that accept HTML strings.
  • Client-side code that reads URL fragments or storage and then manipulates the DOM outside React's normal rendering model.

Next.js XSS risks

Next.js inherits React's rendering model but adds server rendering, server components, client components, route parameters, metadata generation, script loading, middleware, and deployment-specific CSP considerations. This creates more places where trust boundaries need to be explicit.

A common mistake is assuming server-rendered HTML is automatically safer. If a server component retrieves untrusted HTML from a CMS and passes it into a raw rendering path, the fact that rendering happened on the server does not make the content trustworthy. The browser still receives markup.

Route and search parameter handling

Next.js applications commonly use route parameters and search parameters to customize rendering. Normal React interpolation is safe for text, but risk can return if those values are used to construct raw HTML, client-side script data, redirect URLs, or third-party widget configuration without context-appropriate validation.

Next.js and CSP

Next.js applications can implement nonce-based or hash-based CSP, but framework-generated scripts and dynamic rendering mean policy design should be treated as architecture rather than a single header change. Test the policy in Report-Only mode before enforcement, and verify the public response after CDN or proxy processing.

Vue XSS risks

Vue escapes normal text interpolation and many attribute bindings. The main raw-HTML escape hatch is v-html, which renders HTML directly. Vue's security guidance emphasizes that non-trusted templates and raw HTML require care because the framework cannot distinguish intended markup from malicious markup once the application explicitly asks it to render HTML.

<p>{{ userComment }}</p>

<div v-html="trustedHtml"></div>

The first line treats the value as text. The second delegates trust to application code. A security review should identify where trustedHtml originates and what sanitization policy makes it trustworthy.

Vue patterns that deserve review

  • v-html with content from users, APIs, or CMS systems.
  • User-controlled templates or component definitions.
  • Unvalidated URLs in dynamic bindings.
  • Custom directives that manipulate raw DOM content.
  • Third-party rich-text plugins that return HTML.

Angular XSS risks

Angular applies contextual sanitization to many template bindings and escapes values by default. It also exposes APIs that let developers mark values as trusted. Those trust-bypass APIs are powerful and should be rare because they move responsibility from Angular's sanitizer to application code.

A dangerous pattern is bypassing Angular security because a value "looks safe" during testing. If the underlying data source later changes, the bypass remains. Prefer normal template bindings and Angular's built-in security model unless there is a reviewed reason to do otherwise.

URLs are part of the XSS attack surface

Framework escaping does not mean every URL is safe. Applications frequently build navigation links, image sources, iframe sources, or redirect targets from user-controlled data. Validate allowed schemes and, where appropriate, allowed destinations.

Do not assume that because a URL value contains no angle brackets it cannot create script execution or unsafe navigation. The correct defense depends on how the browser uses the URL and which protocols the component accepts.

CMS, rich text, and Markdown are common trust boundaries

Many modern applications need formatted content. A headless CMS, Markdown parser, or WYSIWYG editor often produces HTML that the front end then renders. This is one of the most common reasons teams reach for raw HTML APIs.

The secure design should answer a specific question: where is the content sanitized, with which allowlist, and which component is allowed to render the result? Sanitizing in several unrelated places creates inconsistent behavior. Sanitizing nowhere and calling content trusted because it came from the CMS is even worse.

Hypothetical scenario: one CMS, three front ends

A marketing team stores product descriptions in a headless CMS. The CMS permits headings, lists, links, and emphasis. A React storefront renders the HTML with dangerouslySetInnerHTML. A Vue administration preview uses v-html. A Next.js page server-renders the same content for SEO.

The security mistake would be assuming the CMS itself makes the content trusted. If an editor account is compromised, an import pipeline is abused, or the CMS accepts unsafe attributes, the same malicious content can reach several applications.

A stronger design sanitizes the content at a defined boundary with a narrow allowlist, strips scripting attributes and dangerous URL schemes, and treats only the sanitized representation as eligible for raw rendering. Regression tests should cover the sanitizer configuration as well as each renderer.

Third-party components can bypass safe defaults

A framework application can be secure in its own templates and still inherit risk from a date picker, chart library, rich-text editor, analytics widget, or legacy plugin that manipulates the DOM directly. Review components that accept HTML, templates, render callbacks, or arbitrary URLs.

Keep those dependencies current and remove unused components. The JS Library Vulnerability Checker can help identify visible frontend library and version signals on a deployed site, but public detection is not a replacement for source-level dependency management.

How to test framework applications for XSS

1. Inventory escape hatches

Search the codebase for raw HTML APIs, trust bypass functions, direct DOM manipulation, Markdown renderers, rich-text libraries, and URL construction.

2. Trace content sources

For each escape hatch, identify where content originates. User input, CMS data, imported feeds, support tickets, webhook payloads, and third-party APIs should be considered untrusted until a control establishes otherwise.

3. Test the deployed rendering context

A value can be safe as text but unsafe as HTML or a URL. Test both initial rendering and later client-side updates, especially in hydrated or route-driven applications.

4. Review CSP

Use the Vulnify CSP Checker and Security Headers Analyzer to inspect the public policy. Treat the policy as an additional layer, not proof that raw rendering is safe.

Remediation patterns that scale

  • Replace raw HTML with normal text rendering when formatting is unnecessary.
  • Sanitize rich content at a clearly documented boundary.
  • Prefer allowlists over attempts to blacklist dangerous strings.
  • Validate protocols for user-controlled links and media URLs.
  • Remove trust-bypass calls that exist only to silence framework warnings.
  • Keep framework and sanitizer dependencies maintained.
  • Test server-rendered and client-rendered states separately.
  • Add unit and end-to-end tests for approved raw-HTML paths.

How Vulnify fits into framework XSS testing

Vulnify can help assess the public behavior of deployed applications. The Website Vulnerability Scanner and Website Security Scanner can surface public-facing XSS-related behavior, while the XSS Payload List provides defensive test references.

Framework-specific XSS still benefits from code review because an external scanner cannot see every raw-rendering decision, sanitizer policy, or internal trust boundary. Use public testing to validate what an attacker can reach, then trace the result back to framework code.

Framework XSS checklist

  • Search React for dangerouslySetInnerHTML.
  • Search Vue for v-html.
  • Review Angular trust-bypass APIs.
  • Review Next.js server-rendered HTML and CSP design.
  • Inventory Markdown, rich-text, and CMS renderers.
  • Validate user-controlled URL protocols.
  • Review direct DOM manipulation and third-party widgets.
  • Keep sanitizers maintained and narrowly configured.
  • Retest both initial render and dynamic client updates.

Frequently asked questions

Is React safe from XSS by default?

React's normal text rendering is safer than raw string-based HTML construction, but applications can reintroduce XSS through raw HTML, unsafe URLs, direct DOM manipulation, and vulnerable third-party components.

Does Angular sanitize everything automatically?

No. Angular protects many normal bindings, but developers can bypass those protections. Security still depends on where data comes from and which APIs are used.

Is Vue v-html always a vulnerability?

No. It is a raw HTML rendering feature. It becomes dangerous when the HTML is untrusted or inadequately sanitized.

Does server rendering remove XSS risk?

No. Server rendering changes where markup is produced, but the browser still interprets the final document. Untrusted HTML remains untrusted regardless of where it was assembled.

Conclusion

Modern frameworks reduce common XSS mistakes, but they do not remove trust decisions from application design. The highest-value review is to find every place the framework's default escaping is bypassed, trace where that content originates, and verify the sanitizer or validation boundary. If raw HTML is unnecessary, remove it. If it is necessary, constrain it deliberately and test the deployed result.