# React Router Releases This page lists all releases/release notes for React Router back to `v7.0.0`. - For v6.x releases, please refer to the [v6](https://github.com/remix-run/react-router/blob/v6/CHANGELOG.md) branch - For releases prior to v6, please refer to the [Github Releases Page](https://github.com/remix-run/react-router/releases) We manage release notes in this file instead of the paginated Github Releases Page for 2 reasons: - Pagination in the Github UI means that you cannot easily search release notes for a large span of releases at once - The paginated Github interface also cuts off longer releases notes without indication in list view, and you need to click into the detail view to see the full set of release notes
Table of Contents - [React Router Releases](#react-router-releases) - [v8.5.0](#v850) - [View Transition Deprecations](#view-transition-deprecations) - [API-only Mode (unstable)](#api-only-mode-unstable) - [Custom Fetch and Request Metadata (unstable)](#custom-fetch-and-request-metadata-unstable) - [Application-Controlled Route Discovery (unstable)](#application-controlled-route-discovery-unstable) - [v8.4.0](#v840) - [Reduced Route Component Re-renders](#reduced-route-component-re-renders) - [More Efficient Route Matching (unstable)](#more-efficient-route-matching-unstable) - [v8.3.1](#v831) - [v8.3.0](#v830) - [RSC Entry Updates](#rsc-entry-updates) - [v8.2.0](#v820) - [Web Streams Default Server Entry](#web-streams-default-server-entry) - [v8.1.0](#v810) - [Agent Skills Installation via `create-react-router`](#agent-skills-installation-via-create-react-router) - [Observability Metadata](#observability-metadata) - [v8.0.1](#v801) - [v8.0.0](#v800) - [Baseline Support](#baseline-support) - [Adopted Future Flag Behavior](#adopted-future-flag-behavior) - [Removed `react-router-dom`](#removed-react-router-dom) - [Removed deprecated `meta` `data` fields](#removed-deprecated-meta-data-fields) - [Cloudflare Vite Plugin](#cloudflare-vite-plugin) - [`@react-router/architect` `useRequestContextDomainName`](#react-routerarchitect-userequestcontextdomainname) - [Pre-rendering Flow](#pre-rendering-flow) - [React Router v7 Releases](#react-router-v7-releases) - [v7.18.0](#v7180) - [CSRF Check Logic Fix](#csrf-check-logic-fix) - [v7.17.0](#v7170) - [v7.16.0](#v7160) - [v7.15.1](#v7151) - [v7.15.0](#v7150) - [Stabilizations](#stabilizations) - [Route matching optimizations](#route-matching-optimizations) - [v7.14.2](#v7142) - [v7.14.1](#v7141) - [v7.14.0](#v7140) - [v7.13.2](#v7132) - [Pass-through Requests (unstable)](#pass-through-requests-unstable) - [Route handlers/middleware `unstable_url` parameter](#route-handlersmiddleware-unstable_url-parameter) - [v7.13.1](#v7131) - [URL Masking (unstable)](#url-masking-unstable) - [v7.13.0](#v7130) - [v7.12.0](#v7120) - [v7.11.0](#v7110) - [`vite preview` Support](#vite-preview-support) - [Stabilized Client-side `onError`](#stabilized-client-side-onerror) - [Call-site Revalidation Opt-out (unstable)](#call-site-revalidation-opt-out-unstable) - [v7.10.1](#v7101) - [v7.10.0](#v7100) - [Stabilized `future.v8_splitRouteModules`](#stabilized-futurev8_splitroutemodules) - [Stabilized `future.v8_viteEnvironmentApi`](#stabilized-futurev8_viteenvironmentapi) - [Stabilized `fetcher.reset()`](#stabilized-fetcherreset) - [Stabilized `DataStrategyMatch.shouldCallHandler()`](#stabilized-datastrategymatchshouldcallhandler) - [v7.9.6](#v796) - [v7.9.5](#v795) - [Instrumentation (unstable)](#instrumentation-unstable) - [v7.9.4](#v794) - [`useRoute()` (unstable)](#useroute-unstable) - [v7.9.3](#v793) - [v7.9.2](#v792) - [RSC Framework Mode (unstable)](#rsc-framework-mode-unstable) - [Fetcher Reset (unstable)](#fetcher-reset-unstable) - [v7.9.1](#v791) - [v7.9.0](#v790) - [Stable Middleware and Context APIs](#stable-middleware-and-context-apis) - [v7.8.2](#v782) - [v7.8.1](#v781) - [v7.8.0](#v780) - [Consistently named `loaderData` values](#consistently-named-loaderdata-values) - [Improvements/fixes to the middleware APIs (unstable)](#improvementsfixes-to-the-middleware-apis-unstable) - [v7.7.1](#v771) - [v7.7.0](#v770) - [Unstable RSC APIs](#unstable-rsc-apis) - [v7.6.3](#v763) - [v7.6.2](#v762) - [v7.6.1](#v761) - [v7.6.0](#v760) - [`routeDiscovery` Config Option](#routediscovery-config-option) - [Automatic Types for Future Flags](#automatic-types-for-future-flags) - [v7.5.3](#v753) - [v7.5.2](#v752) - [v7.5.1](#v751) - [v7.5.0](#v750) - [`route.lazy` Object API](#routelazy-object-api) - [v7.4.1](#v741) - [v7.4.0](#v740) - [v7.3.0](#v730) - [v7.2.0](#v720) - [Type-safe `href` utility](#type-safe-href-utility) - [Prerendering with a SPA Fallback](#prerendering-with-a-spa-fallback) - [Allow a root `loader` in SPA Mode](#allow-a-root-loader-in-spa-mode) - [v7.1.5](#v715) - [v7.1.4](#v714) - [v7.1.3](#v713) - [v7.1.2](#v712) - [v7.1.1](#v711) - [v7.1.0](#v710) - [v7.0.2](#v702) - [v7.0.1](#v701) - [v7.0.0](#v700)
## v8.5.0 Date: 2026-10-16 ### What's Changed #### View Transition Deprecations React Router's view transition APIs are now marked deprecated in favor of React's [``](https://react.dev/reference/react/ViewTransition) component, available in React 19.3+. The existing APIs will continue to work for the duration of React Router v8 and will be removed in v9. To use ``, wrap it around an `` in a persistent layout: ```tsx import { ViewTransition } from "react"; import { Outlet } from "react-router"; export default function Layout() { return ( ); } ``` When migrating, remove the `viewTransition` props/options and replace `useViewTransitionState`, `NavLink`'s `isTransitioning`, and `.transitioning` styles with React's View Transition classes. Note using `` requires React transitions be enabled, so do not set `useTransitions={false}` on your router component. See the [migration guide](https://reactrouter.com/how-to/view-transitions) for examples and compatibility considerations. #### API-only Mode (unstable) Framework Mode can now serve static HTML while retaining a runtime server for loaders, actions, revalidation, and lazy route discovery. This allows you to deploy your static HTML files to a CDN on one origin while having them hit server loader/actions on a runtime server potentially on another origin. Enable API-only Mode in `react-router.config.ts`: ```ts import type { Config } from "@react-router/dev/config"; export default { ssr: "unstable_api-only", // Optionally serve complete HTML for selected paths: prerender: ["/", "/about"], } satisfies Config; ``` If you are running your API server on a separate origin, set `unstable_apiServerOrigin` in your config to route API calls to the proper origin (and ensure you configure CORS and `allowedActionOrigins` appropriately). See [Rendering Strategies](https://reactrouter.com/start/framework/rendering#api-only-mode-unstable) for deployment details. #### Custom Fetch and Request Metadata (unstable) In Framework Mode, pass `unstable_fetch` to `` in `app/entry.client.tsx` to customize JavaScript-issued server data and route discovery requests: ```tsx import { hydrateRoot } from "react-dom/client"; import { HydratedRouter, type unstable_RouterFetch } from "react-router/dom"; const customFetch: unstable_RouterFetch = (request, context) => { let headers = new Headers(request.headers); headers.set("X-App-Request-Type", context.type); return fetch(request, { headers }); }; hydrateRoot(document, ); ``` The callback receives a constructed `Request` and context identifying initialization, navigation, fetcher, revalidation, or manifest requests. Navigation-initiated data requests also include `navigationType` (`"PUSH"`, `"REPLACE"`, or `"POP"`). Data Mode data strategies receive equivalent metadata through `unstable_initiator` and `unstable_navigationType`. See [Custom Fetch](https://reactrouter.com/how-to/custom-fetch) and [Data Strategy](https://reactrouter.com/how-to/data-strategy#request-metadata-unstable) for more information. #### Application-Controlled Route Discovery (unstable) In Framework Mode, enable `future.unstable_customRouteDiscovery` to discover selected destinations or complete the running client's route manifest: ```ts import type { Config } from "@react-router/dev/config"; export default { future: { unstable_customRouteDiscovery: true }, } satisfies Config; ``` ```tsx import { Outlet, unstable_useRouteDiscovery } from "react-router"; function Layout() { unstable_useRouteDiscovery({ async onManifestMismatch(event) { await event.loadAllRoutes(); }, }); return ; } ``` The hook exposes `discoverRoutes(paths)`, `loadAllRoutes()`, and a shared `state` (`"partial"`, `"loading"`, or `"complete"`). Discovery loads route metadata without navigating, running loaders/actions, or prefetching route modules. With lazy discovery, custom mismatch handlers can load the running client's full manifest or let the pending operation cancel while preserving the current screen and data. See [Application-Controlled Route Discovery](https://reactrouter.com/how-to/route-discovery) for cancellation and recovery details. ### Minor Changes - `react-router` - Deprecate React Router's view transition APIs in favor of React's `` component, available in React 19.3 and later ([#15493](https://github.com/remix-run/react-router/pull/15493)) - Deprecate the `viewTransition` props/options, `useViewTransitionState`, and `NavLink`'s `isTransitioning` render prop and `transitioning` class - Preserve existing behavior, including support for older React versions - See the [migration guide](https://reactrouter.com/how-to/view-transitions) for a navigation example and compatibility considerations ### Patch Changes - `react-router` - Fix `` failing to server render promises decoded from an RSC payload, which caused the Suspense boundary to switch to client rendering ([#15597](https://github.com/remix-run/react-router/pull/15597)) - `react-router` - Declare `@types/react` and `@types/react-dom` as optional peer dependencies so the shipped type definitions resolve React types under strict (non-hoisted) node_modules layouts ([#15528](https://github.com/remix-run/react-router/pull/15528)) - `react-router` - Fix the `unstable_useRouterState` documentation example to call the imported alias ([#15535](https://github.com/remix-run/react-router/pull/15535)) - `react-router` - Render the `HydrateFallback` of a route discovered through `patchRoutesOnNavigation` during initial hydration ([#15537](https://github.com/remix-run/react-router/pull/15537)) - Routes discovered during initial hydration are now exposed in `state.matches` while their loaders run, so `` renders down to the discovered route's `HydrateFallback` instead of rendering nothing - `react-router` - Fix router state recovery when updates occur before `RouterProvider` subscribes, including when other subscribers are already attached, and report current client errors once without replaying server errors during hydration ([#15637](https://github.com/remix-run/react-router/pull/15637)) - `react-router` - Fix `matchPath` not treating a static segment as optional when it contains characters outside `[\w-]` ([#15425](https://github.com/remix-run/react-router/pull/15425)) - `react-router` - Fix an RSC server crash when a client aborts a document request while the HTML stream waits for the RSC payload ([#15612](https://github.com/remix-run/react-router/pull/15612)) - `react-router` - Stop revalidating a fetcher after `fetcher.reset()`, so its cleared data no longer comes back on the next revalidation ([#15633](https://github.com/remix-run/react-router/pull/15633)) - `@react-router/dev` - Fix critical CSS collection in dev registering server-only route imports as client dependencies, which triggered dependency re-optimization and full page reloads ([#15595](https://github.com/remix-run/react-router/pull/15595)) - `@react-router/dev` - Fix intermittent `Prerender: Request failed for ... connect ECONNREFUSED` errors during prerendering ([#15325](https://github.com/remix-run/react-router/pull/15325)) - Send requests to the address the Vite preview server bound to - Avoid re-resolving `localhost` for each request, which could select a different address family (`127.0.0.1` vs `::1`) - `@react-router/express` - Fix the request URL losing its port when the `Host` or `X-Forwarded-Host` header is an IPv6 address (e.g., `[::1]:3000`) ([#15559](https://github.com/remix-run/react-router/pull/15559)) - `@react-router/node` - Write file session data to a temporary file and rename it into place so concurrent commits of the same session can no longer corrupt its file ([#15620](https://github.com/remix-run/react-router/pull/15620)) - Preserve existing session file permissions when replacing the file ### Unstable Changes ⚠️ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_ - `react-router` - Add an `unstable_fetch` prop to `` to customize JavaScript-issued manifest and server data requests in conventional Framework Mode ([#15586](https://github.com/remix-run/react-router/pull/15586)) - The function receives the intended request as well as context identifying the type of request - Data request context includes `navigationType` (`"PUSH"`, `"REPLACE"`, or `"POP"` for navigation-initiated requests, including reloaded fetchers; `null` otherwise) - Data strategies receive equivalent metadata through `unstable_initiator` and `unstable_navigationType` - The custom fetch context is a discriminated union, so narrowing `type` to `"navigation"` guarantees a non-null `navigationType`, while other data requests have a `null` `navigationType` - Fetchers reloaded after an initialization redirect retain the `"initialization"` type and include their fetcher key - Like other router initialization options, the implementation is captured when the singleton router is created and thus subsequent prop changes are ignored - Browser-managed data prefetches from `` use native `` elements and do not call the custom implementation - Use the callback for custom fetch options, custom headers, safe request retries, or response header inspection - Return response bodies unchanged so React Router can decode its manifest and Turbo Stream data protocols - `react-router` - Support API-only Framework Mode with runtime server data loading, actions, revalidation, and lazy route discovery ([#15601](https://github.com/remix-run/react-router/pull/15601)) - Hydrate prerendered pages from build-time data and fetch server loader data when hydrating a SPA fallback - Preserve normal loader revalidation and server-action support for eagerly loaded and lazily discovered routes, and honor loader opt-outs when making server data requests - `react-router` - Add `unstable_useRouteDiscovery` for application-controlled route discovery in conventional Framework Mode ([#15602](https://github.com/remix-run/react-router/pull/15602)) - Discover selected destinations or load the running client's full route manifest without loading route modules - Compose manifest-mismatch handlers, with shared recovery and cancellation that preserves application state - Keep full-manifest failures catchable without rendering error boundaries, whether mismatch handlers use the hook or event's `loadAllRoutes()` - Cancel pending revalidation alongside a canceled navigation, retaining existing data and settling interrupted fetchers before restoring Back/Forward history - Enable `future.unstable_customRouteDiscovery` to opt in - Apps without the flag retain existing discovery behavior - Call `event.defaultBehavior()` from a custom mismatch handler to explicitly run built-in recovery - `react-router` - Return middleware short-circuit responses as-is for document requests in RSC modes, including resource routes, instead of decoding non-RSC bodies as Flight ([#15628](https://github.com/remix-run/react-router/pull/15628)) - Include server middleware in both eager and lazy RSC route configuration types - Preserve HTML rendering for cached Flight responses returned by middleware, and pass through raw responses that replace the result of `next()` - `@react-router/dev` - Add `ssr: "unstable_api-only"` to retain a runtime server for loaders, actions, and route discovery while disabling production document rendering ([#15601](https://github.com/remix-run/react-router/pull/15601)) - Generate a root-only SPA shell when prerendering is disabled - Support prerendered pages and data files alongside runtime loaders and actions, with a SPA fallback for non-prerendered paths - Default API-only builds to lazy route discovery - Reject API-only configuration in RSC Framework Mode - Preserve server-only route exports in additional Vite server environments in both API-only and SSR modes - Serve prerendered pages and the SPA fallback in Vite preview while retaining live data requests and route discovery - `@react-router/dev` - Add the `future.unstable_customRouteDiscovery` flag to opt into application-controlled route discovery in conventional Framework Mode ([#15602](https://github.com/remix-run/react-router/pull/15602)) - `@react-router/dev` - Fix source maps for route modules in unstable RSC Framework Mode ([#15479](https://github.com/remix-run/react-router/pull/15479)) - `@react-router/dev` - Add `unstable_apiServerOrigin` to configure the origin for API-only loader, action, and route discovery requests ([#15601](https://github.com/remix-run/react-router/pull/15601)) - `@react-router/serve` - Support API-only builds in `react-router-serve` by serving client documents and forwarding data and route discovery requests to the React Router runtime ([#15601](https://github.com/remix-run/react-router/pull/15601)) - Serve prerendered documents when available and use the generated SPA fallback for non-prerendered paths **Full Changelog**: [`v8.4.0...v8.5.0`](https://github.com/remix-run/react-router/compare/react-router@8.4.0...react-router@8.5.0) ## v8.4.0 Date: 2026-09-15 ### What's Changed #### Reduced Route Component Re-renders In 8.4.0, we reworked our internal data router contexts to be more granular which will reduce re-renders for components using hooks to access only some router state. For example, your component won't automatically re-render when `navigation.state` transitions to `loading` if it only calls `useLocation`. 4 internal contexts are now used: - Current location info (`useLocation`, `useSearchParams`, `useMatches`) - Pending navigation/revalidation info (`useNavigation`, `useRevalidation`) - Router loader/action data (`useLoaderData`, `useActionData`) - Fetcher info (`useFetcher`, `useFetchers`) #### More Efficient Route Matching (unstable) Data Mode can now opt into a more efficient route matcher powered by `@remix-run/route-pattern`. Synthetic Chromium benchmarks reduced navigation and fetcher completion times by ~19–38% with 100 routes and ~71–88% with 1,000 routes, excluding network latency and React rendering. Route definitions and path generation APIs such as `generatePath` and `href` continue to use React Router path syntax. Before enabling the flag, call `unstable_preloadRoutePattern()` so applications that do not opt in avoid downloading the new matcher. This also comes with a new `unstable_validateParams` route field that accepts keyed regular expressions. When a parameter fails validation, matching continues to later routes. Optional parameters that are not present are not validated. ```ts import { createBrowserRouter } from "react-router"; import { unstable_preloadRoutePattern } from "react-router/route-pattern"; unstable_preloadRoutePattern(); let router = createBrowserRouter( [ { path: "/:drink", unstable_validateParams: { drink: /^(wines|whiskeys|sakes|beers)$/, }, }, { path: "/:food", unstable_validateParams: { food: /^(meats|veggies|cheeses|sweets)$/, }, }, ], { future: { unstable_routePatternMatching: true, }, }, ); ``` The legacy matching APIs (`matchRoutes`, `matchPath`, and `useMatch`) continue to use the previous regex-based matcher and may produce slightly different results. Use the new `router.match()` API when you need to match with the opted-in router. This API is private and unstable along with the flag, and will stabilize as the first-class matching API when the flag stabilizes. ### Minor Changes - `react-router` - Deprecate the `createStaticRouter({ branches })` option ([#15297](https://github.com/remix-run/react-router/pull/15297)) - `createStaticRouter` now caches route branches internally, ignores `branches`, and logs a deprecation warning when the option is provided - The deprecated `EntryContext.branches` property remains available for compatibility but is always an empty array ### Patch Changes - `react-router` - Prevent stale route discovery during manifest version-mismatch recovery ([#15489](https://github.com/remix-run/react-router/pull/15489)) - Keep concurrent manifest responses pending while a document reload is in progress - Report a discovery error when a previous reload failed to resolve a version mismatch instead of loading a stale route or reloading repeatedly - Fail pending requests if a document reload does not complete within five seconds or the document is restored from the back-forward cache, allowing subsequent requests to recover - `react-router` - Preserve lazy route module import errors during SPA navigations instead of replacing them with a missing `dataStrategy` result error ([#15464](https://github.com/remix-run/react-router/pull/15464)) - `react-router` - Switch to more granular internal router contexts to avoid unnecessary route component re-renders when unrelated data router state changes ([#15376](https://github.com/remix-run/react-router/pull/15376)) - ⚠️ This contains some breaking changes to exported `UNSAFE_` contexts, so please review carefully if you are using those unsafe exports - `react-router` - Correctly escape streamed RSC redirect locations in meta tag attributes ([#15491](https://github.com/remix-run/react-router/pull/15491)) - `react-router` - Avoid unintended `document.startViewTransition` calls during initial hydration and `router.revalidate()` calls ([#15484](https://github.com/remix-run/react-router/pull/15484)) - `react-router` - Fix `SingleFetchNoResultError` thrown when a fetcher revalidates against a splat route during lazy route discovery ([#15395](https://github.com/remix-run/react-router/pull/15395)) - Track discovery per fetcher load so revalidation waits for the current load's discovery, even when the fetcher key is reused, while still restarting interrupted loaders after discovery completes - `react-router` - Preserve the underlying decode failure as the `cause` of the `Unable to decode turbo-stream response` error ([#15450](https://github.com/remix-run/react-router/pull/15450)) - `@react-router/dev` - Fix action submissions behind an HTTPS reverse proxy in `react-router dev` and `vite preview` by using `X-Forwarded-Proto` when constructing `request.url` ([#15466](https://github.com/remix-run/react-router/pull/15466)) - No React Router configuration is required - The URL host continues to come from `Host`, not `X-Forwarded-Host` - Configure reverse proxies to overwrite client-supplied `X-Forwarded-Proto` headers - Do not expose dev or preview servers publicly - `@react-router/dev` - Pass development conditions as CLI flags when relaunching the dev process ([#15435](https://github.com/remix-run/react-router/pull/15435)) - `@react-router/dev` - Fix stack trace locations for split route modules when source maps are enabled in the `reactRouter` Vite plugin ([#15440](https://github.com/remix-run/react-router/pull/15440)) - `@react-router/node` - Fix a memory leak in `writeReadableStreamToWritable` and `writeAsyncIterableToWritable` that retained memory for the lifetime of a long-running stream ([#15500](https://github.com/remix-run/react-router/pull/15500)) - Preserve error handling when a producer closes the destination while reading the next chunk ### Unstable Changes ⚠️ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_ - `react-router` - Add a Data Mode `future.unstable_routePatternMatching` flag for more efficient route matching powered by `@remix-run/route-pattern` ([#15298](https://github.com/remix-run/react-router/pull/15298)) - Add an `unstable_validateParams` route field to reject invalid parameter values and continue matching - `react-router` - Document access control requirements for RSC Server Functions ([#15490](https://github.com/remix-run/react-router/pull/15490)) - Treat every Server Function as a public endpoint that must perform all of its own access control checks - Recommend route actions when access control should be provided by route middleware **Full Changelog**: [`v8.3.1...v8.4.0`](https://github.com/remix-run/react-router/compare/react-router@8.3.1...react-router@8.4.0) ## v8.3.1 Date: 2026-08-27 ### Patch Changes - `react-router` - Fix `Expected fetcher: ` error thrown on navigation when a fetcher is aborted during its post-action revalidation ([#15365](https://github.com/remix-run/react-router/pull/15365)) - `react-router` - Fix lazy route discovery caching a path as discovered when the triggering navigation was aborted after the manifest response settled but before the route tree was patched, which permanently (for the session) shadowed the real route behind a catch-all or produced 404s on every subsequent visit ([#15399](https://github.com/remix-run/react-router/pull/15399)) - `react-router` - Improve route matching performance for long paths ([#15417](https://github.com/remix-run/react-router/pull/15417)) - `react-router` - Improve validation of action request origins ([#15419](https://github.com/remix-run/react-router/pull/15419)) - `react-router` - Fix `` leaving `history.scrollRestoration` set to `"auto"` after a bfcache restore, which let the browser restore scroll on subsequent history traversals before the destination route had rendered ([#15397](https://github.com/remix-run/react-router/pull/15397)) - `react-router` - Properly respect the `relative` option in `useSubmit`/`fetcher.submit` when resolivng the `action` path ([#15400](https://github.com/remix-run/react-router/pull/15400)) - `react-router` - Add additional URL validation on client side navigations/redirects ([#15445](https://github.com/remix-run/react-router/pull/15445)) - `@react-router/dev` - Bump `@remix-run/node-fetch-server` dependency ([#15447](https://github.com/remix-run/react-router/pull/15447)) - `@react-router/dev` - Generate JavaScript entry files at package build time so `react-router reveal --no-typescript` does not require Prettier at runtime ([#15373](https://github.com/remix-run/react-router/pull/15373)) - Deprecate the `--no-typescript` flag ahead of its removal in React Router v9 - `@react-router/node` - Bump `@remix-run/node-fetch-server` dependency ([#15447](https://github.com/remix-run/react-router/pull/15447)) - `@react-router/node` - fix: Prevent client disconnects during streaming from crashing the Node process ([#15324](https://github.com/remix-run/react-router/pull/15324)) - `@react-router/serve` - Bump `@remix-run/node-fetch-server` dependency ([#15447](https://github.com/remix-run/react-router/pull/15447)) - `@react-router/serve` - Serve `/.well-known/*` files from the client build directory. Express 5's static middleware ignores every dot-segment path by default, so RFC 8615 well-known URIs — ACME challenges, Android's `assetlinks.json`, Apple's `apple-app-site-association` — fell through to the request handler and came back as app-rendered HTML instead of the static file. Other dotfiles remain hidden. ([#15340](https://github.com/remix-run/react-router/pull/15340)) **Full Changelog**: [`v8.3.0...v8.3.1`](https://github.com/remix-run/react-router/compare/react-router@8.3.0...react-router@8.3.1) ## v8.3.0 Date: 2026-07-22 ### What's Changed #### RSC Entry Updates This release includes several updates for unstable RSC apps that use custom entry files. Apps using the default RSC Framework entries do not need any changes. If you maintain custom RSC entries, review the generated unstable change notes for the new client version, subresource integrity, and CSP nonce wiring. Custom `entry.rsc.tsx` files should pass the generated client version to `unstable_matchRSCServerRequest`, and custom `entry.ssr.tsx` files may need to pass the generated import map integrity data and request nonce through React's HTML renderer. ### Minor Changes - `@react-router/dev` - Restart `react-router dev` with `--conditions=development` when not already configured ([#15291](https://github.com/remix-run/react-router/pull/15291)) ### Patch Changes - `react-router` - Encode path params in `href`/`generatePath` per RFC 3986 path-segment rules instead of `encodeURIComponent` ([#15310](https://github.com/remix-run/react-router/pull/15310)) - Characters that are valid literally in a path segment (`$ & + , ; = : @` — RFC 3986 `pchar`) are no longer percent-encoded, so values like a semver build `1.0.0+1` interpolate unchanged instead of becoming `1.0.0%2B1` - Structural/unsafe characters (`/ ? # %`, whitespace, non-ASCII) are still escaped exactly as before - `react-router` - Use `crypto.randomUUID()` for `createMemorySessionStorage` session ids ([#15302](https://github.com/remix-run/react-router/pull/15302)) - `createMemorySessionStorage` is only intended for local development and testing - sessions are lost when the server restarts - `react-router` - Fix `NavLink` not applying its `pending` state when `to` has a trailing slash ([#15300](https://github.com/remix-run/react-router/pull/15300)) - `@react-router/architect` - Allow `typescript@7` to be used ([#15317](https://github.com/remix-run/react-router/pull/15317)) - `@react-router/cloudflare` - Allow `typescript@7` to be used ([#15317](https://github.com/remix-run/react-router/pull/15317)) - `@react-router/dev` - Allow `typescript@7` to be used ([#15317](https://github.com/remix-run/react-router/pull/15317)) - `@react-router/express` - Allow `typescript@7` to be used ([#15317](https://github.com/remix-run/react-router/pull/15317)) - `@react-router/fs-routes` - Allow `typescript@7` to be used ([#15317](https://github.com/remix-run/react-router/pull/15317)) - `@react-router/node` - Allow `typescript@7` to be used ([#15317](https://github.com/remix-run/react-router/pull/15317)) - `@react-router/remix-routes-option-adapter` - Allow `typescript@7` to be used ([#15317](https://github.com/remix-run/react-router/pull/15317)) ### Unstable Changes ⚠️ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_ - `react-router` - Preserve RSC route component metadata so routes with a `clientLoader` can skip unnecessary server requests once their components have rendered while still fetching missing server-rendered elements ([#15323](https://github.com/remix-run/react-router/pull/15323)) - `react-router` - Harden RSC CSRF code paths ([#15311](https://github.com/remix-run/react-router/pull/15311)) - `react-router` - Fix server crash (`TypeError: Invalid state: Unable to enqueue`) when a request is aborted while the RSC HTML stream has a pending flush ([#15286](https://github.com/remix-run/react-router/pull/15286)) - Handle cancellation of the `injectRSCPayload` readable side, clear the pending flush, and cancel the underlying RSC payload stream - `react-router` - Detect stale RSC clients during lazy route discovery and reload the destination document ([#15318](https://github.com/remix-run/react-router/pull/15318)) #### Migration Apps using the default RSC Framework entry do not need to make any changes. Apps with a custom `entry.rsc.tsx` should import the generated client version and pass it to `unstable_matchRSCServerRequest`: ```tsx import clientVersion from "virtual:react-router/unstable_rsc/client-version"; return unstable_matchRSCServerRequest({ // ... clientVersion, }); ``` - `react-router` - Add CSP nonce support to RSC document rendering ([#15320](https://github.com/remix-run/react-router/pull/15320)) - Add `nonce` options to `unstable_routeRSCServerRequest` and `unstable_RSCStaticRouter` - Forward the nonce to the HTML renderer and apply it to injected RSC payload scripts and nonce-aware framework components To adopt nonce-based CSP, update your `entry.ssr.tsx` (run `react-router reveal entry.ssr` first in RSC Framework Mode) to generate a fresh nonce for each request. Pass it to `routeRSCServerRequest`, spread the `renderHTML` options into React's HTML renderer, pass `options.nonce` to `RSCStaticRouter`, and use the same nonce in the `Content-Security-Policy` response header: ```tsx const nonce = crypto.randomUUID(); const response = await routeRSCServerRequest({ request, serverResponse, createFromReadableStream, nonce, async renderHTML(getPayload, options) { const payload = getPayload(); return renderHTMLToReadableStream( , { ...options, bootstrapScriptContent, formState: await payload.formState, signal: request.signal, }, ); }, }); response.headers.set( "Content-Security-Policy", `script-src 'self' 'nonce-${nonce}'`, ); ``` - `@react-router/dev` - Add `unstable_rsc/client-version` client build version virtual module ([#15318](https://github.com/remix-run/react-router/pull/15318)) - `@react-router/dev` - Support the `subResourceIntegrity` config option in RSC Framework Mode ([#15321](https://github.com/remix-run/react-router/pull/15321)) #### Migration guide No changes are required when using the default RSC SSR entry. If you maintain a custom `app/entry.ssr.tsx`, import the new virtual module and pass its hashes to React's `importMap` render option: ```diff +import subResourceIntegrity from "virtual:react-router/unstable_rsc/subresource-integrity"; return renderToReadableStream(, { ...options, bootstrapScriptContent, formState, + importMap: subResourceIntegrity + ? { integrity: subResourceIntegrity } + : undefined, signal: request.signal, }); ``` **Full Changelog**: [`v8.2.0...v8.3.0`](https://github.com/remix-run/react-router/compare/react-router@8.2.0...react-router@8.3.0) ## v8.2.0 Date: 2026-07-08 ### What's Changed #### Web Streams Default Server Entry Non-Node runtime Framework Mode apps no longer need a custom `entry.server.tsx` file using React's `renderToReadableStream` API. Apps with `@react-router/{node,express,serve}` dependencies will continue to default to `renderToPipeableStream`, while non-Node apps default to `renderToReadableStream`. Because Web Streams are stable in Node 22+, Node apps can also opt-into the Web Streams default entry with the new `future.unstable_enableNodeReadableStream` flag: ```ts filename=react-router.config.ts import type { Config } from "@react-router/dev/config"; export default { future: { unstable_enableNodeReadableStream: true, }, } satisfies Config; ``` This flag has no effect if you have a custom `entry.server.tsx` keep using their custom entry file. It only applies to the default entry used if one doesn't exist. Node apps opting-into the Web Streams API _might_ even see a small performance boost because React Router already uses Web Streams internally, so this avoids additional conversions between Web/Node streams. If you see perf changes one way or another upon adopting this flag, please let us know! ### Minor Changes - `@react-router/dev` - Add a Web Streams default server entry for non-Node Framework mode apps ([#15290](https://github.com/remix-run/react-router/pull/15290)) - Apps using `@react-router/node`, `@react-router/express`, or `@react-router/serve` continue to use the `renderToPipeableStream` default server entry - Apps without those Node server adapter dependencies use a `renderToReadableStream` default server entry - Non-Node apps with their own `entry.server.tsx` may be able to remove it in favor of the default if it is not doing anything custom - `@react-router/dev` - Detect `nub` as a supported package manager when installing framework dependencies ([#15276](https://github.com/remix-run/react-router/pull/15276)) - `create-react-router` - Detect `nub` as a supported package manager when creating new projects ([#15276](https://github.com/remix-run/react-router/pull/15276)) ### Patch Changes - `react-router` - Fix `href()` to properly stringify and URL-encode param values, matching `generatePath()` ([#15277](https://github.com/remix-run/react-router/pull/15277)) - splat params preserve path separators while encoding each segment individually - `react-router` - Fix dynamic param extraction for routes with optional static segments ([#15200](https://github.com/remix-run/react-router/pull/15200)) - When a route path contains optional static segments (e.g. `/school?/user/:id`), the internal regex's incorrectly shifted parameter indices resulting in incorrect parameter extraction - Consecutive optional static segments (e.g. `/one?/two?`) were only partially handled - `react-router` - Preserve navigation blocker state through a revalidation ([#15246](https://github.com/remix-run/react-router/pull/15246)) - `react-router` - Fix route ranking for dynamic parameters with static extension suffixes ([#15273](https://github.com/remix-run/react-router/pull/15273)) - These were not being detected as dynamic param segments and instead got incorrectly scored higher as a static segment - This meant they could potentially tie truly static routes like `/sitemap.xml` and outrank them based on definition order - These are now correctly identified as dynamic parameter segments and scored correctly - `react-router` - Use ReactFormState types instead of unknown ([#15263](https://github.com/remix-run/react-router/pull/15263)) - `@react-router/dev` - Detect user `rolldownOptions` config in Vite 8+ ([#15278](https://github.com/remix-run/react-router/pull/15278)) ### Unstable Changes ⚠️ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_ - `@react-router/dev` - Add the [`future.unstable_enableNodeReadableStream`](https://reactrouter.com/upgrading/future#futureunstable_enablenodereadablestream) flag to opt Node Framework mode apps into using `renderToReadableStream` instead of `renderToPipeableStream` ([#15290](https://github.com/remix-run/react-router/pull/15290)) - This flag has no effect if you have your own `entry.server.tsx` **Full Changelog**: [`v8.1.0...v8.2.0`](https://github.com/remix-run/react-router/compare/react-router@8.1.0...react-router@8.2.0) ## v8.1.0 Date: 2026-06-29 ### What's Changed #### Agent Skills Installation via `create-react-router` `create-react-router` can now setup the React Router [Agent Skill](https://github.com/remix-run/react-router/tree/main/.agents/skills/react-router) in your new project. Interactive shells will issue a prompt on whether to include the skills, and they will be included by default with when running with `--yes` or in non-interactive shells. You can skip the skill addition with the `--no-agent-skills` CLI flag. #### Observability Metadata The Instrumentation APIs `info` parameter usually corresponds roughly to the inputs to the thing being instrumented (`handler`, `loader`, etc.). For route level instrumentations such as loaders, this contains useful information like the `pattern` (i.e., `/blog/:slug`) that allows you to report information that is easily aggregated by pattern, instead of having to manually deduce one from the request url. However, for outer layers such as the `handler` or a router `navigate` call - we can't provide a `pattern` because we haven't yet done any route matching, so it wasn't easy to report at those levels based on a generic route pattern. The internal instrumentation results now contain relevant metadata in `result.meta` for these outer instrumentation layers. For server `handler` instrumentations, we also expose the `statusCode` of the outgoing HTTP response: ```ts export const instrumentations = [ { handler(handler) { handler.instrument({ async request(handleRequest) { let result = await handleRequest(); // Available to server `handler`, and router `navigate`/`fetch` instrumentations let normalizedUrl = result.meta?.url; let routePattern = result.meta?.pattern; let routeParams = result.meta?.params; // Available to server `handler` only let statusCode = result.statusCode; }, }); }, }, ]; ``` Please see the [docs](https://reactrouter.com/how-to/instrumentation#result-metadata) for more information. ### Minor Changes - `react-router` - Return route metadata from server request, client navigation, and client fetcher instrumentations ([#15235](https://github.com/remix-run/react-router/pull/15235)) - Adds result metadata after instrumented calls complete, including the URL, matched route pattern, and params - Adds known HTTP status codes to server request handler instrumentation results - `create-react-router` - Add a default-on CLI option to include the official React Router agent skill in generated projects ([#15213](https://github.com/remix-run/react-router/pull/15213)) - New projects include `.agents/skills/react-router` by default when running with `--yes` or in non-interactive shells - Interactive runs prompt to include the skill, defaulting to yes - Use `--no-agent-skills` to skip copying the skill ### Patch Changes - `@react-router/dev` - Fix a regression with the new prerendering plugin where the `react-router.config.ts` `buildEnd` hook would run before prerendering was completed ([#15211](https://github.com/remix-run/react-router/pull/15211)) - `@react-router/dev` - Fixed `react-router typegen` crashes under the Bun runtime when Babel default imports are already unwrapped ([#15214](https://github.com/remix-run/react-router/pull/15214)) - `@react-router/dev` - Replace the deprecated `envFile:false` Vite config with `envDir:false` to eliminate a deprecation warning when using vite@8.1.0+ ([#15230](https://github.com/remix-run/react-router/pull/15230)) - `@react-router/dev` - Only add the `"node"` Vite server condition for Framework mode apps that declare a Node server adapter dependency ([#15242](https://github.com/remix-run/react-router/pull/15242)) - This prevents non-Node SSR runtimes from resolving Node-specific package exports by default - `@react-router/serve` - Use Node's built-in networking APIs to find an available port and remove the `get-port` dependency ([#15239](https://github.com/remix-run/react-router/pull/15239)) - `create-react-router` - Use Node's built-in utilities for CLI argument parsing, ANSI-stripping, and child process execution to remove the `arg`, `strip-ansi`, and `execa` dependencies ([#15231](https://github.com/remix-run/react-router/pull/15231)) **Full Changelog**: [`v8.0.1...v8.1.0`](https://github.com/remix-run/react-router/compare/react-router@8.0.1...react-router@8.1.0) ## v8.0.1 Date: 2026-06-18 ### Patch Changes - `react-router` - Remove the obsolete `AppLoadContext` type export accidentally left over from v7 now that middleware is always enabled and server request context is provided through `RouterContextProvider`. ([#15207](https://github.com/remix-run/react-router/pull/15207)) **Full Changelog**: [`v8.0.0...v8.0.1`](https://github.com/remix-run/react-router/compare/react-router@8.0.0...react-router@8.0.1) ## v8.0.0 Date: 2026-06-17 ### What's Changed React Router v8 is here! We introduced a new [Open Governance](https://remix.run/blog/rr-governance) model last year and this marks the first major release on our new planned yearly major release cadence. We chose the June timeframe this year to align with the EOL timeframe for Node 20. Node 22 is [scheduled](https://nodejs.org/en/about/previous-releases) to reach EOL in the May 2027 timeframe so we'll be aiming for a v9 release around the same time next year. Our [API Development Strategy](https://reactrouter.com/community/api-development-strategy) aims to make major releases relatively boring by introducing breaking changes ahead of time behind [Future Flags](https://reactrouter.com/upgrading/v7). If you've adopted all active future flags in v7, then from a React Router API surface you're in good shape for v8. All `future.v8_*` flags have been removed (or lifted to a top-level config) and their behaviors are now the default. #### Baseline Support React Router v8 updates the following minimum supported versions: - Node 22.22.0+ - Starting with v8, React Router will officially support all Active LTS node versions and only the **latest** minor branch of Maintenance LTS versions - This better allows us to bump minimum Maintenance LTS versions to account for newly released security patches - It also allows us to more quickly and easily adopt new Active LTS features backported to Maintenance LTS lines - Upgraded minimum Maintenance LTS versions will be done in React Router minor releases - React 19.2.7+ - Vite 7+ To modernize the library, React Router is now published as an ESM-only module and tsconfig `target`/`lib` fields have been updated to ES2022 across the board #### Adopted Future Flag Behavior The following v8 future flags have been removed and their behaviors are now the default: - `future.v8_trailingSlashAwareDataRequests` - `future.v8_passThroughRequests` - `future.v8_middleware` - `future.v8_viteEnvironmentApi` - `future.v8_splitRouteModules` has been moved to a to a top-level `splitRouteModules` config option and is enabled by default #### Removed `react-router-dom` In v7, we collapsed the DOM APIs into `react-router/dom`, but to ease the v6->v7 upgrade we continued re-exporting everything through `react-router-dom`. We have now dropped `react-router-dom`, so if you didn't get around to swapping your imports in v7, you will need to swap them to `react-router` and `react-router/dom` for v8. #### Removed deprecated `meta` `data` fields The `data` fields passed to route module `meta` functions were deprecated in v7 and are remove din v8. Use `loaderData` instead of `data` on `MetaArgs` and each item in `MetaArgs.matches`. #### Cloudflare Vite Plugin The React Router Cloudflare dev proxy (`@react-router/dev/vite/cloudflare`) has been removed in v8. Cloudflare projects should use [`@cloudflare/vite-plugin`](https://developers.cloudflare.com/workers/vite-plugin/) instead. #### `@react-router/architect` `useRequestContextDomainName` The `@react-router/architect` `createRequestHandler` `useRequestContextDomainName` option has been removed as that is now the default behavior in v8. #### Pre-rendering Flow In v7 we had a `future.unstable_previewServerPrerendering` flag that would opt you into a new pre-rendering flow using the Vite preview server (leveraging the Vite environment API). Now that the Vite environment API is always available on our new Vite 7+ baseline, we dropped this flag and the preview server flow has replaced our old pre-rendering implementation. It _should_ be a non-breaking change but if you see issues, please let us know! ### Major Changes - `react-router` - Update minimum Node version to 22.22.0 ([#14928](https://github.com/remix-run/react-router/pull/14928)) - `react-router` - Update minimum React version to 19.2.7 ([#15062](https://github.com/remix-run/react-router/pull/15062)) - `react-router` - Remove the `future.v8_trailingSlashAwareDataRequests` flag ([#15100](https://github.com/remix-run/react-router/pull/15100)) - Trailing slash-aware data request URLs are now the default behavior. - `react-router` - Remove `future.v8_passThroughRequests` flag - the raw incoming `request` is now always passed through to `loader`/`action`. Use `url` for the normalized URL without React Router-specific implementation details (`.data` suffixes, `index`/`_routes` search params). ([#15079](https://github.com/remix-run/react-router/pull/15079)) - `react-router` - Remove `future.v8_middleware` flag — middleware is always enabled in v8 ([#15078](https://github.com/remix-run/react-router/pull/15078)) - The `future.v8_middleware` flag has been removed; middleware is now always enabled - The `context` parameter passed to `loader`, `action`, and `middleware` functions is always a `RouterContextProvider` instance - `getLoadContext` functions in custom servers must return a `RouterContextProvider` — returning a plain object is no longer supported - The `MiddlewareEnabled` type (previously exported as `UNSAFE_MiddlewareEnabled`) has been removed since the conditional it gated is now unconditional - The `Future` module augmentation pattern (`interface Future { v8_middleware: true }`) is no longer needed to type `context` in Data Mode - `react-router` - Remove `future.v8_passThroughRequests` flag - the raw incoming `request` is now always passed through to `loader`/`action`. ([#15079](https://github.com/remix-run/react-router/pull/15079)) - `react-router` - Move `future.v8_splitRouteModules` to a top-level `splitRouteModules` config option and change the default behavior to `true` ([#15086](https://github.com/remix-run/react-router/pull/15086)) - Set `splitRouteModules: false` to keep route modules in a single chunk - Set `splitRouteModules: "enforce"` to require all routes to be splittable - `@react-router/dev` - Removed the `future.v8_viteEnvironmentApi` flag because the Vite Environment API is always enabled ([#15077](https://github.com/remix-run/react-router/pull/15077)) - `@react-router/dev` - Removed the `future.unstable_previewServerPrerendering` flag and make prerendering with the Vite Environment API the default. ([#15077](https://github.com/remix-run/react-router/pull/15077)) - `react-router` - Update `tsconfig.json` `target`/`lib` from `ES2020 -> ES2022` ([591853e](https://github.com/remix-run/react-router/commit/591853e)) - `react-router` - Switch the published packages in `packages/` to ESM-only. ([#14895](https://github.com/remix-run/react-router/pull/14895)) ([59ebcf1](https://github.com/remix-run/react-router/commit/59ebcf1)) - `react-router` - Remove deprecated `data` parameter in favor of `loaderData` for `meta` APIs (to align with `Route.ComponentProps`) ([#14931](https://github.com/remix-run/react-router/pull/14931)) - `Route.MetaArgs`, `Route.MetaMatch`, `MetaArgs`, `MetaMatch`, `Route.ComponentProps.matches`, `UIMatch` - `react-router` - Remove internal `hasErrorBoundary` field added to `router.routes` when using a data router ([#15074](https://github.com/remix-run/react-router/pull/15074)) - This should not impact user-facing code since this was an internal prop and was computed based on the presence of `ErrorBoundary` or `errorElement` on your route - `hasErrorBoundary` is no longer accepted on `RouteObject` (`IndexRouteObject`/`NonIndexRouteObject`), `DataRouteObject`, `` JSX props, or as a key in `lazy` route definitions. - The `MapRoutePropertiesFunction` signature no longer requires returning `hasErrorBoundary`; the router infers it directly. - `react-router` - Remove `react-router-dom` package ([#15076](https://github.com/remix-run/react-router/pull/15076)) - In v7 everything DOM-specific was collapsed into `react-router/dom` - `react-router-dom` was kept around as a convenience so existing v6 app imports would still work - For v8, you will need to swap `react-router-dom` imports: - `RouterProvider`/`HydratedRouter` should be imported from `react-router/dom` - Everything else should be imported from `react-router` - `@react-router/architect` - Bump `@architect/functions` to v8 ([#15106](https://github.com/remix-run/react-router/pull/15106)) - `@react-router/architect` - Remove the `useRequestContextDomainName` option from `createRequestHandler` - this is now the default behavior ([#15188](https://github.com/remix-run/react-router/pull/15188)) - `@react-router/dev` - Remove `@react-router/dev/vite/cloudflare` dev proxy export; use `@cloudflare/vite-plugin` instead ([#15077](https://github.com/remix-run/react-router/pull/15077)) - Drops support for `wrangler@3` as a peer dependency of `@react-router/dev` - `@react-router/dev` - Require Vite 7+ and make the Vite Environment API build path mandatory ([#15077](https://github.com/remix-run/react-router/pull/15077)) - `@react-router/express` - Bump dependencies ([#15106](https://github.com/remix-run/react-router/pull/15106)) - Bumped `express` from `^4.19.2` to `^4.22.2` - Bumped the `express` peer dependency from `^4.17.1 || ^5` to `^4.22.2 || ^5` - Bumped `@types/express` from `^4.17.9` to `^4.17.25` - `@react-router/node` - Switch from `@mjackson/node-fetch-server` to `@remix-run/node-fetch-server` now that we can directly use ESM-only packages ([#14930](https://github.com/remix-run/react-router/pull/14930)) - `@react-router/serve` - Switch from `@mjackson/node-fetch-server` to `@remix-run/node-fetch-server` now that we can directly use ESM-only packages ([#14930](https://github.com/remix-run/react-router/pull/14930)) - `create-react-router` - Switch from `@remix-run/web-fetch` to native `fetch` internally. ([#14929](https://github.com/remix-run/react-router/pull/14929)) - This removes the underlying `HTTPS_PROXY` support that `node-fetch` and subsequently `@remix-run/web-fetch` supported ### Minor Changes - `react-router` - Bump dependencies ([#15080](https://github.com/remix-run/react-router/pull/15080)) - Bumped `cookie` from `^1.0.1` to `^1.1.1` - Bumped `set-cookie-parser` from `^2.6.0` to `^3.1.0` - `@react-router/cloudflare` - Bump `@cloudflare/workers-types` fromn `^4.20260520.1` to `^4.20260527.1` ([#15106](https://github.com/remix-run/react-router/pull/15106)) - `@react-router/dev` - Bump dependencies ([#15080](https://github.com/remix-run/react-router/pull/15080)) - Bumped `@babel/core` from `^7.27.7` to `^7.29.7` - Bumped `@babel/generator` from `^7.27.5` to `^7.29.7` - Bumped `@babel/parser` from `^7.27.7` to `^7.29.7` - Bumped `@babel/plugin-syntax-jsx` from `^7.27.1` to `^7.29.7` - Bumped `@babel/preset-typescript` from `^7.27.1` to `^7.29.7` - Bumped `@babel/traverse` from `^7.27.7` to `^7.29.7` - Bumped `@babel/types` from `^7.27.7` to `^7.29.7` - Bumped `dedent` from `^1.5.3` to `^1.7.2` - Bumped `jsesc` from `3.0.2` to `3.1.0` - Bumped `lodash` from `^4.17.21` to `^4.18.1` - Bumped `prettier` from `^3.6.2` to `^3.8.3` - Bumped `@remix-run/node-fetch-server` from `^0.13.0` to `^0.13.3` - Bumped `react-refresh` from `^0.14.0` to `^0.18.0` - Bumped `semver` from `^7.3.7` to `^7.8.1` - Bumped `tinyglobby` from `^0.2.14` to `^0.2.16` - Bumped `valibot` from `^1.2.0` to `^1.4.1` - `@react-router/dev` - Replace `cookie` and `set-cookie-parser` with `cookie-es` ([#15109](https://github.com/remix-run/react-router/pull/15109)) - `@react-router/dev` - Removed the `vite-node` dependency in favor of Vite's native module runner APIs ([#15104](https://github.com/remix-run/react-router/pull/15104)) - `@react-router/serve` - Bump `express` from `4.21.2` to `5.2.1` ([#15101](https://github.com/remix-run/react-router/issues/15101)) - `create-react-router` - Bump dependencies ([#15080](https://github.com/remix-run/react-router/pull/15080)) - Bumped `execa` from `5.1.1` to `9.6.1` - Bumped `log-update` from `^5.0.1` to `^8.0.0` - Bumped `semver` from `^7.3.7` to `^7.8.1` - Bumped `sort-package-json` from `^1.55.0` to `^3.6.1` - Bumped `strip-ansi` from `^6.0.1` to `^7.2.0` - Bumped `tar-fs` from `^2.1.3` to `^3.1.2` ### Patch Changes - `react-router` - Ensure client middleware errors load lazy route error boundaries before bubbling ([#15086](https://github.com/remix-run/react-router/pull/15086)) - `react-router` - Remove explicit `onSubmit` type override from `SharedFormProps` to fix deprecation warning with `@types/react@19.x` ([#14932](https://github.com/remix-run/react-router/pull/14932)) ([59ebcf1](https://github.com/remix-run/react-router/commit/59ebcf1)) - `react-router` - Update package builds to preserve individual module files in published artifacts. Public APIs and documented import paths are unchanged. ([#15092](https://github.com/remix-run/react-router/pull/15092)) - Updated package TypeScript configs to support modern module syntax used by the build configuration. - `react-router` - Migrate package builds from `tsup` to `tsdown`. Published package entry points and public APIs are unchanged. ([#15092](https://github.com/remix-run/react-router/pull/15092)) - `react-router` - Upgrade React Router's TypeScript tooling to TypeScript 6. Runtime behavior and public APIs are unchanged. ([#15092](https://github.com/remix-run/react-router/pull/15092)) - `@react-router/architect` - Bump dependencies ([#15080](https://github.com/remix-run/react-router/pull/15080)) - Bumped `@types/aws-lambda` from `^8.10.82` to `^8.10.161` - `@react-router/dev` - Bump dependencies ([#15080](https://github.com/remix-run/react-router/pull/15080)) - Bumped `@babel/core` from `^7.29.0` to `^7.29.7` - Bumped `@babel/generator` from `^7.29.1` to `^7.29.7` - Bumped `@babel/parser` from `^7.29.3` to `^7.29.7` - Bumped `@babel/plugin-syntax-jsx` from `^7.28.6` to `^7.29.7` - Bumped `@babel/preset-typescript` from `^7.28.5` to `^7.29.7` - Bumped `@babel/traverse` from `^7.29.0` to `^7.29.7` - Bumped `@babel/types` from `^7.29.0` to `^7.29.7` - Bumped `babel-dead-code-elimination` from `^1.0.6` to `^1.0.12` - Bumped `chokidar` from `^4.0.0` to `^5.0.0` - Bumped `es-module-lexer` from `^1.3.1` to `^2.1.0` - Bumped `exit-hook` from `2.2.1` to `5.1.0` - Bumped `isbot` from `^5.1.11` to `^5.1.40` - Bumped `p-map` from `^7.0.3` to `^7.0.4` - Bumped `pathe` from `^1.1.2` to `^2.0.3` - Bumped `pkg-types` from `^2.3.0` to `^2.3.1` - Bumped `react-refresh` from `^0.14.0` to `^0.18.0` - Bumped `semver` from `^7.8.0` to `^7.8.1` - Bumped `tinyglobby` from `^0.2.14` to `^0.2.16` - Bumped `valibot` from `^1.4.0` to `^1.4.1` - `@react-router/dev` - Fix Windows libuv assertion (`!(handle->flags & UV_HANDLE_CLOSING)` in `src/win/async.c`) during prerendering by using `node:http` instead of `fetch` for internal prerender requests against the Vite preview server ([#15077](https://github.com/remix-run/react-router/pull/15077)) - `@react-router/fs-routes` - Bump dependencies ([#15091](https://github.com/remix-run/react-router/pull/15091)) - Bumped `minimatch` from `^9.0.0` to `^10.2.5` - `@react-router/node` - Bump dependencies ([#15106](https://github.com/remix-run/react-router/pull/15106)) - Bumped `@remix-run/node-fetch-server` from `^0.13.0` to `^0.13.3` - `@react-router/serve` - Bump dependencies ([#15091](https://github.com/remix-run/react-router/pull/15091)) - Bumped `@remix-run/node-fetch-server` from `^0.13.0` to `^0.13.3` - Bumped `get-port` from `5.1.1` to `7.2.0` **Full Changelog**: [`v7.18.0...v8.0.0`](https://github.com/remix-run/react-router/compare/react-router@7.18.0...react-router@8.0.0) # React Router v7 Releases ## v7.18.0 Date: 2026-06-16 ### What's Changed #### CSRF Check Logic Fix We made a bug fix in our underlying CSRF checks in this release that may be a "breaking bug fix" for some users deployed behind a reverse proxy. The CSRF check now checks directly against the `host` in the `request` url provided, instead of looking directly at HTTP headers which is an adapter concern. If your adapter is not setting the expected host in the request URL, you may need to add the new internal host to your `allowedActionOrigins` config. This is most likely to occur in `@react-router/serve` apps or `@react-router/express` apps without the `trust proxy` setting enabled. We recommend testing this against application mutation requests as part of your upgrade. ### Minor Changes - `@react-router/architect` - Add a `useRequestContextDomainName` option to `createRequestHandler` to derive request URL hosts from the API Gateway request context ([#15185](https://github.com/remix-run/react-router/pull/15185)) - This flag will become the default behavior in v8, so it is recommended to adopt to prepare for and to v8 better align with your deployment architecture and rely less on manual header parsing in the adapter - See the [docs](https://reactrouter.com/api/other-api/adapter#react-routerarchitect) for more information ### Patch Changes - `react-router` - Fix server handler prerender responses when using `ssr: false` and `future.v8_trailingSlashAwareDataRequests: true` ([#15173](https://github.com/remix-run/react-router/pull/15173)) - Avoids false positive "SPA Mode" detection when serving prerendered paths - `react-router` - Use the `ServerRouter` `nonce` for nonce-aware SSR components when they don't provide their own value so strict CSP pages can load them ([#15170](https://github.com/remix-run/react-router/pull/15170)) - `react-router` - Use `turbo-stream` to serialize and deserialize Framework Mode hydration errors ([#15175](https://github.com/remix-run/react-router/pull/15175)) - `react-router` - Optimize route matching by extending precomputed route branches to include matchers ([#15186](https://github.com/remix-run/react-router/pull/15186)) - `react-router` - Use the constructed `request` URL `host` instead of header checks when validating action request origins in the CSRF check ([#15185](https://github.com/remix-run/react-router/pull/15185)) - `react-router` - Remove the un-documented custom error serialization logic from Data Mode SSR built-in hydration flows ([#15175](https://github.com/remix-run/react-router/pull/15175)) - `react-router` - Validate protocols in RSC render redirects ([#15177](https://github.com/remix-run/react-router/pull/15177)) - `react-router` - Consolidate url normalization logic and better handle mixed slashes ([#15176](https://github.com/remix-run/react-router/pull/15176)) - `@react-router/dev` - Pass Vite `server.watch` config to child compiler in development mode. ([#15178](https://github.com/remix-run/react-router/pull/15178)) - `@react-router/dev` - Ignore external Vite server environments in Framework Mode build hooks ([#14883](https://github.com/remix-run/react-router/pull/14883)) - When `future.v8_viteEnvironmentApi` is enabled, React Router previously treated any non-client Vite environment as its own server build - This caused issues with integrations like Nitro, where plugins can register additional environments - Framework Mode build hooks now ignore external server environments and only process the app's own server build - `@react-router/express` - Adjust express adapter host computation ([#15185](https://github.com/remix-run/react-router/pull/15185)) - read port from `x-forwarded-host` based on `trust proxy` setting - handle invalid hostname characters **Full Changelog**: [`v7.17.0...v7.18.0`](https://github.com/remix-run/react-router/compare/react-router@7.17.0...react-router@7.18.0) ## v7.17.0 Date: 2026-06-04 ### Minor Changes - `react-router` - Ship a subset of the official documentation inside the `react-router` package ([#15121](https://github.com/remix-run/react-router/pull/15121)) - Markdown docs are now available in `node_modules/react-router/docs`, letting AI coding agents and the React Router agent skills read official docs locally - Excludes auto-generated API docs (`api/`), `community/` content, and tutorials (`tutorials/`) ### Patch Changes - `@react-router/dev` - Fix future flag warning URLs and only log each future flag warning one time ([#15138](https://github.com/remix-run/react-router/pull/15138)) ### Unstable Changes ⚠️ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_ - `@react-router/dev` - Prevent RSC route module server exports from being scanned by the client dependency optimizer when `future.unstable_optimizeDeps` is enabled. ([#15005](https://github.com/remix-run/react-router/pull/15005)) **Full Changelog**: [`v7.16.0...v7.17.0`](https://github.com/remix-run/react-router/compare/react-router@7.16.0...react-router@7.17.0) ## v7.16.0 Date: 2026-05-28 ### Minor Changes - `react-router` - Stabilize `future.unstable_trailingSlashAwareDataRequests` as `future.v8_trailingSlashAwareDataRequests` ([#15098](https://github.com/remix-run/react-router/pull/15098)) - `@react-router/dev` - Stabilize `future.unstable_trailingSlashAwareDataRequests` as `future.v8_trailingSlashAwareDataRequests` ([#15098](https://github.com/remix-run/react-router/pull/15098)) - The unstable flag is no longer supported and will error during config resolution - `@react-router/dev` - Log future flag warnings for upcoming React Router v8 flags ([#15029](https://github.com/remix-run/react-router/pull/15029)) - `v8_middleware`, `v8_splitRouteModules`, `v8_viteEnvironmentApi`, `v8_passThroughRequests`, `v8_trailingSlashAwareDataRequests` ### Patch Changes - `react-router` - Disable manifest path when lazy route dicovery is disabled ([#15068](https://github.com/remix-run/react-router/pull/15068)) - `react-router` - Fix browser URL creation to use the configured history window instead of the global window. ([#15066](https://github.com/remix-run/react-router/pull/15066)) - Pass the history/router window through to `createBrowserURLImpl` so custom window contexts keep the correct URL origin. - `react-router` - Fix `useNavigation()` return type to preserve discriminated union across navigation states ([#15095](https://github.com/remix-run/react-router/pull/15095)) - `react-router` - Widen `MetaDescriptor` `script:ld+json` type from `LdJsonObject` to `LdJsonObject | LdJsonObject[]` to permit multiple JSON-LD schemas in a single `