React escapes text you put in JSX. So <p>{comment}</p> shows <script> as plain text, and the browser never runs it. React also sets attribute values as strings, so a quote in a value can't break out of the attribute. But React can only protect the places it controls.
XSS can still happen in these places:
dangerouslySetInnerHTML: React inserts the string as real HTML, with no checks.- URL attributes:
href,src,actionandformActionacceptjavascript:URLs. React escapes the value, but the URL still runs when someone clicks it. Don't count on React to block these. Check the scheme yourself. - Direct DOM access: code like
ref.current.innerHTML = htmlskips React completely. - Spreading props from data:
<div {...userProps}>lets data from the server setdangerouslySetInnerHTMLor other attributes. - Server rendering: state written into a
<script>tag with plainJSON.stringifycan break out of the tag. - Libraries: markdown renderers, chart tooltips and rich-text editors often use
innerHTMLinside. evalandnew Functionon user data.
In short: React makes the default path safe. The escape hatches and the URL attributes are where you must sanitize or validate.
- React escapes text and attribute values that you put in JSX.
dangerouslySetInnerHTMLinserts raw HTML with no checks.hrefandsrccan still runjavascript:URLs, so check the scheme.- Refs with
innerHTML, spread props from data and third-party libraries skip React's escaping. - Server-rendered state inside a
<script>tag needs special escaping.
Read more
- dangerouslySetInnerHTML (react.dev) (https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html)react.dev
- Cross Site Scripting Prevention Cheat Sheet (OWASP) (https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)cheatsheetseries.owasp.org