# `react-router`
## v8.3.0
### Patch Changes
- 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
- 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
- Fix `NavLink` not applying its `pending` state when `to` has a trailing slash ([#15300](https://github.com/remix-run/react-router/pull/15300))
- 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))
- Harden RSC CSRF code paths ([#15311](https://github.com/remix-run/react-router/pull/15311))
- 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
### Unstable Changes
⚠️ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
- 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,
});
```
- 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}'`,
);
```
## v8.2.0
### Patch Changes
- 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
- 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
- Preserve navigation blocker state through a revalidation ([#15246](https://github.com/remix-run/react-router/pull/15246))
- 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
- Use ReactFormState types instead of unknown ([#15263](https://github.com/remix-run/react-router/pull/15263))
## v8.1.0
### Minor Changes
- 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
## v8.0.1
### Patch Changes
- 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))
## v8.0.0
### Major Changes
- 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.
- Update `tsconfig.json` `target`/`lib` from `ES2020 -> ES2022` ([591853e](https://github.com/remix-run/react-router/commit/591853e))
- 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))
- 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`
- 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))
- 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.
- 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`
- 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
- Update minimum Node version to 22.22.0 ([#14928](https://github.com/remix-run/react-router/pull/14928))
- Update minimum React version to 19.2.7 ([#15062](https://github.com/remix-run/react-router/pull/15062))
### Minor Changes
- 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`
### Patch Changes
- Ensure client middleware errors load lazy route error boundaries before bubbling ([#15086](https://github.com/remix-run/react-router/pull/15086))
- 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))
- 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.
- 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))
- 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))
## v7.18.0
### Patch Changes
- Fix server handler prerender responses when using `ssr: false` and `future.v8_trailingSlashAwareDataRequests: true`. Avoids false positive "SPA Mode" detection when serving prerendered paths ([#15173](https://github.com/remix-run/react-router/pull/15173))
- 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))
- Use `turbo-stream` to serialize and deserialize Framework Mode hydration errors ([#15175](https://github.com/remix-run/react-router/pull/15175))
- Precompute route branch matchers to avoid recompiling route path regexes during matching ([#15186](https://github.com/remix-run/react-router/pull/15186))
- Use the constructed request URL host when validating action request origins. ([#15185](https://github.com/remix-run/react-router/pull/15185))
- 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))
- Validate protocols in RSC render redirects ([#15177](https://github.com/remix-run/react-router/pull/15177))
- Consolidate url normalization logic and better handle mixed slashes ([#15176](https://github.com/remix-run/react-router/pull/15176))
## v7.17.0
### Minor Changes
- 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/`)
## v7.16.0
### Minor Changes
- Stabilize `future.unstable_trailingSlashAwareDataRequests` as `future.v8_trailingSlashAwareDataRequests` ([#15098](https://github.com/remix-run/react-router/pull/15098))
### Patch Changes
- Disable manifest path when lazy route dicovery is disabled ([#15068](https://github.com/remix-run/react-router/pull/15068))
- 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.
- Fix `useNavigation()` return type to preserve discriminated union across navigation states ([#15095](https://github.com/remix-run/react-router/pull/15095))
- Widen `MetaDescriptor` `script:ld+json` type from `LdJsonObject` to `LdJsonObject | LdJsonObject[]` to permit multiple JSON-LD schemas in a single `