{"record":{"id":"3a60645b7b3e1dec","repo":"vercel/next.js","slug":"next-js-has-blocked-a-javascript-url-as-a-securit","errorCode":null,"errorMessage":"Next.js has blocked a javascript: URL as a security precaution.","messagePattern":"Next\\.js has blocked a javascript: URL as a security precaution\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/next/src/client/components/app-router-instance.ts","lineNumber":340,"sourceCode":"    url: new URL(href),\n    historyState,\n  })\n}\n\n/**\n * (Experimental) Perform a gesture navigation. This dispatches through React's\n * useOptimistic instead of the main action queue, allowing the state to be\n * shown during a gesture transition and discarded when the canonical navigation\n * completes.\n *\n * Only available when experimental.gestureTransition is enabled.\n */\nfunction gesturePush(href: string, options?: NavigateOptions): void {\n  if (process.env.__NEXT_GESTURE_TRANSITION) {\n    // TODO: Trigger a prefetch so the cache starts populating if there isn't\n    // already a prefetch for this route.\n    if (isJavaScriptURLString(href)) {\n      throw new Error(\n        'Next.js has blocked a javascript: URL as a security precaution.'\n      )\n    }\n\n    const state = getCurrentAppRouterState()\n    if (state === null) {\n      return\n    }\n    const url = new URL(addBasePath(href), location.href)\n    if (isExternalURL(url)) {\n      return\n    }\n\n    // Fork the router state for the duration of the gesture transition.\n    const currentUrl = new URL(state.canonicalUrl, location.href)\n    const scrollBehavior =\n      options?.scroll === false\n        ? ScrollBehavior.NoScroll","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/vercel/next.js/blob/0ae8c72462952df163f1b1e0726641bc5b40dc93/packages/next/src/client/components/app-router-instance.ts#L322-L358","documentation":"The experimental `gesturePush` method on the App Router throws when the href is a `javascript:` URL. Next.js blocks javascript: URLs in all navigation methods as a security precaution against XSS — a javascript: href could execute arbitrary code. This guard protects gesture-based navigation (enabled via experimental.gestureTransition).","triggerScenarios":"Passing a string starting with `javascript:` to the gesture navigation API (gesturePush), which is triggered when experimental.gestureTransition is enabled.","commonSituations":"Dynamic href values from untrusted or user-generated content (CMS, API data); legacy `javascript:void(0)` placeholder links; bookmarklet-style URLs; a data binding bug that injects a javascript: scheme.","solutions":["Replace the javascript: URL with a valid route path or '#'.","Sanitize and validate all dynamic href values before passing to router navigation methods.","If you need a no-op link, use `href='#'` with an onClick handler that calls preventDefault.","Strip or reject any href matching /^javascript:/i at the data source."],"exampleFix":"// before — blocked\nrouter.gesturePush('javascript:alert(1)')\n\n// after — use a real route or a safe placeholder\nrouter.gesturePush('/dashboard')\n// or for a no-op button:\n<button onClick={(e) => { e.preventDefault(); doSomething() }}>Click</button>","handlingStrategy":"validation","validationCode":"function isJavaScriptURL(href: string): boolean {\n  return /^\\s*javascript:/i.test(href)\n}\nfunction safeHref(href: string): string {\n  return isJavaScriptURL(href) ? '/' : href\n}","typeGuard":"function isSafeNavigationHref(href: unknown): href is string {\n  return typeof href === 'string' && !/^\\s*javascript:/i.test(href)\n}","tryCatchPattern":null,"preventionTips":["Sanitize all dynamic href values at the trust boundary (API parsing, form input).","Never use javascript: URLs in navigation — use route paths or onClick handlers.","Add a lint rule or runtime guard to reject javascript: hrefs in navigation calls."],"tags":["security","xss","router","navigation","javascript-url"],"analyzedSha":"0ae8c72462952df163f1b1e0726641bc5b40dc93","analyzedAt":"2026-08-06T19:44:29.143Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}