React Security Best Practices 2025

Ahmad Sadeddin

CEO at Corgea

React is one of the most popular frameworks used for Web Development. Secure coding in React requires awareness against common web threats like XSS, CSRF, and injection attacks. React’s design helps by automatically escaping dynamic content in JSX, but software developers must follow best practices. Below are essential steps to secure your React apps, each illustrated with code examples.

Default XSS Protection with Data Binding

React’s JSX syntax automatically escapes values when you use curly braces ({}). This means rendering dynamic text via {} is safe against Cross-Site Scripting (XSS) by default. For example:

// Safe rendering: userName is escaped if it contains <script>
<div>{userName}</div>

In contrast, avoid binding untrusted data into HTML attributes without validation. For instance, don’t do:

// Unsafe: attacker could set formAction to “javascript:...”, leading to XSS
<form action={formActionValue}>...</form>

Sanitizing HTML

Sometimes you may need to render raw HTML (e.g. content from a rich-text editor). In React, this requires the dangerouslySetInnerHTML prop, which bypasses automatic escaping. Always sanitize any HTML string before injecting it. For example, use DOMPurify:

import DOMPurify from 'dompurify';

function SafeContent({ htmlString }) {
  // Sanitize the HTML to strip out scripts or unsafe tags
  const cleanHTML = DOMPurify.sanitize(htmlString);
  return <div dangerouslySetInnerHTML={{ __html: cleanHTML }} />;
}

By sanitizing with DOMPurify or a similar library, you ensure any malicious scripts are removed before rendering.

Warning: Do not use dangerouslySetInnerHTML on raw user input without sanitation.

Avoid Direct DOM Manipulation

Directly manipulating the DOM can bypass React’s protective layer and introduce XSS. Avoid accessing elements via ref or ReactDOM.findDOMNode() to set HTML. For example, don’t do:

// BAD: Directly injecting user content into innerHTML 
// can introduce XSS if userContent contains malicious code.
this.myRef.current.innerHTML = userContent;

Instead, rely on React’s rendering. If you must insert HTML dynamically, do it via dangerouslySetInnerHTML after sanitizing (as shown above).

Validating URLs to Prevent Script Injection

Links and URLs in props can be attack vectors if they use the javascript: scheme or other unsafe protocols. Always validate URLs before rendering them. One approach is using the built-in URL parser and checking the protocol:

function isValidUrl(url) {
  try {
    const parsed = new URL(url);
    return ['http:', 'https:'].includes(parsed.protocol);
  } catch {
    return false;
  }
}

// Usage in JSX:
<a href={isValidUrl(userInputUrl) ? userInputUrl : '#'}>Visit site</a>

This ensures only http: or https: links are allowed, preventing javascript:-based injections.

Tip: Never trust raw URLs from users. Either use safe alternatives (e.g., ask for YouTube IDs instead of full URLs) or sanitize them using a library like sanitize-url.

Secure Server-Side Rendering (SSR)

When using React’s server-side rendering (SSR) APIs (renderToString, renderToStaticMarkup), be careful not to mix unsanitized data into the HTML. For example:

app.get('/', (req, res) => {
  const markup = ReactDOMServer.renderToStaticMarkup(<App />);
  // BAD: Concatenating unsanitized data onto SSR output
  const otherData = req.someData; // possibly attacker-controlled
  res.send(markup + otherData);
});

This pattern is unsafe because an attacker could inject scripts into otherData and it would end up in the final HTML. Instead, always sanitize or serialize data safely.

Protecting Serialized State and Preventing JSON Injection

It’s common to embed a JSON blob (e.g., Redux state) into the HTML for hydration. This can open a JSON injection risk if not handled properly. Always escape the < character in your JSON to avoid breaking out of the script context. For example:

<script>
  window.__PRELOADED_STATE__ = \${JSON.stringify(preloadedState).replace(/</g, '\u003c')};
</script>

Dependency Management and Third-Party Libraries

Many security holes come from vulnerabilities in third-party packages. Before adding any new library, scan it for known issues. You can use tools like npm audit:

npm audit            # Lists known vulnerabilities
npm audit fix        # Attempts to automatically fix issues
npx snyk test        # Scans dependencies with Snyk

These checks should be part of your CI/CD pipeline. Additionally, consider integrating a static analysis tool or SAST (Software Composition Analysis) like Corgea to detect insecure patterns in code and dependencies. Corgea can flag uses of unsafe APIs (e.g. dangerouslySetInnerHTML without sanitization) or outdated packages.

Keep Libraries and React Up-to-Date

The React core and its ecosystem occasionally have security fixes. Always use the latest stable versions of react and react-dom. Run npm outdated to see if newer versions are available. Updating dependencies promptly (and testing your app) ensures you are secure from patches against known vulnerabilities. Tools like the Corgea SAST can automate alerting when React or other packages need updates.

Ready be secure?

Harden your software in less than 10 mins'