facebook/react · error · Error

React has blocked a javascript: URL as a security precaution

Error message

React has blocked a javascript: URL as a security precaution.

What it means

sanitizeURL does not throw during rendering; when a URL attribute (href, src, action, formAction, ...) coerces to a string matching the javascript: protocol — including obfuscated forms with control characters, spaces, and whitespace embedded between the letters — React replaces the URL with javascript:throw new Error('React has blocked a javascript: URL as a security precaution.'). The actual Error is thrown in the browser only if that sanitized URL is ever activated (link clicked, form submitted, window opened), neutralizing XSS via URL attributes.

Source

Thrown at packages/react-dom-bindings/src/shared/sanitizeURL.js:29

// and any newline or tab are filtered out as if they're not part of the URL.
// https://url.spec.whatwg.org/#url-parsing
// Tab or newline are defined as \r\n\t:
// https://infra.spec.whatwg.org/#ascii-tab-or-newline
// A C0 control is a code point in the range \u0000 NULL to \u001F
// INFORMATION SEPARATOR ONE, inclusive:
// https://infra.spec.whatwg.org/#c0-control-or-space

const isJavaScriptProtocol =
  /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i;

function sanitizeURL<T>(url: T): T | string {
  // We should never have symbols here because they get filtered out elsewhere.
  // eslint-disable-next-line react-internal/safe-string-coercion
  if (isJavaScriptProtocol.test('' + (url as any))) {
    // Return a different javascript: url that doesn't cause any side-effects and just
    // throws if ever visited.
    // eslint-disable-next-line no-script-url
    return "javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')";
  }
  return url;
}

export default sanitizeURL;

View on GitHub (pinned to eafeac097b)

Solutions

  1. Filter URLs at the source: allowlist schemes (http, https, mailto, tel, relative) before rendering user-supplied hrefs
  2. Use a sanitizer such as DOMPurify or isomorphic-url parsing to validate the protocol and reject non-http(s) links
  3. Replace javascript:void(0) placeholder hrefs with href="#" role/button semantics or a <button>

Example fix

// before
<a href={user.website}>Profile</a> // user.website = "javascript:alert(1)"

// after
const SAFE_URL = /^(https?:|mailto:|tel:|#|\/|\.\/)/i;
const href = SAFE_URL.test(user.website) ? user.website : '#';
<a href={href}>Profile</a>
Defensive patterns

Strategy: validation

Validate before calling

const SAFE_HREF = /^(?:(?:https?|mailto|tel):|#|\/|\.\.?\/)/i;
function safeHref(url: string | null | undefined): string {
  if (url == null) return '#';
  const trimmed = url.trim();
  return SAFE_HREF.test(trimmed) ? trimmed : '#';
}
// <a href={safeHref(user.website)}>

Type guard

function isSafeUrl(url: string): boolean {
  try {
    const u = new URL(url, 'https://example.invalid');
    return ['http:', 'https:', 'mailto:', 'tel:'].includes(u.protocol);
  } catch {
    return false;
  }
}

Try / catch

// The throw fires only in the browser if a sanitized URL is activated; catch it there for telemetry:
window.addEventListener('error', (e) => {
  if (/React has blocked a javascript: URL/.test(String(e.message))) {
    reportSecurityEvent('blocked-javascript-url', e.message);
  }
});

Prevention

When it happens

Trigger: Server-rendering href={`javascript:${code}`}, a link whose href comes from user/CMS content like <a href={user.website}> where user.website is 'javascript:alert(1)', or formAction/action/img src values that begin with javascript: after the leading-control/space prefix the regex allows for.

Common situations: Markdown or rich-text renderers emitting unfiltered URLs; user profile fields rendered as links; test/security scanners probing the sanitizer; accidentally shipping javascript:void(0) hrefs that get blocked in SSR output.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/a14260f54f4c553f. Report an issue: GitHub.