NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #1611 most downloaded on npm
Declarative routing for React
Last release 19 days ago
15 Sep 2026
Ships on a steady schedule
a new release about every 2 weeks
Nearly every release is documented
notes for 60 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
13 years old
1186 releases · first in 2014
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v700
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v700
Date: 2024-11-21
react-router-dom, @remix-run/react, @remix-run/server-runtime, and @remix-run/router have been collapsed into the react-router package
react-router-dom is still published in v7 as a re-export of everything from react-router@remix-run/cloudflare-pages and @remix-run/cloudflare-workers have been collapsed into @react-router/cloudflare package`react-router-dom-v5-compat and react-router-native packages are removed starting with v7Remix v2 used to re-export all common @remix-run/server-runtime APIs through the various runtime packages (node, cloudflare, deno) so that you wouldn't need an additional @remix-run/server-runtime dependency in your package.json. With the collapsing of packages into react-router, these common APIs are now no longer re-exported through the runtime adapters. You should import all common APIs from react-router, and only import runtime-specific APIs from the runtime packages:
// Runtime-specific APIs
import { createFileSessionStorage } from "@react-router/node";
// Runtime-agnostic APIs
import { redirect, useLoaderData } from "react-router";
The following APIs have been removed in React Router v7:
jsondeferunstable_composeUploadHandlersunstable_createMemoryUploadHandlerunstable_parseMultipartFormDataReact Router v7 requires the following minimum versions:
node@20
installGlobals method to polyfill the fetch APIreact@18, react-dom@18Remix and React Router follow an API Development Strategy leveraging "Future Flags" to avoid introducing a slew of breaking changes in a major release. Instead, breaking changes are introduced in minor releases behind a flag, allowing users to opt-in at their convenience. In the next major release, all future flag behaviors become the default behavior.
The following previously flagged behaviors are now the default in React Router v7:
future.v7_relativeSplatPathfuture.v7_startTransitionfuture.v7_fetcherPersistfuture.v7_normalizeFormMethodfuture.v7_partialHydrationfuture.v7_skipActionStatusRevalidationfuture.v3_fetcherPersistfuture.v3_relativeSplatPathfuture.v3_throwAbortReasonfuture.v3_singleFetchfuture.v3_lazyRouteDiscoveryfuture.v3_optimizeDepsThe Remix Vite plugin is the proper way to build full-stack SSR apps using React Router v7. The former esbuild-based compiler is no longer available.
Renamed vitePlugin and cloudflareDevProxyVitePlugin
For Remix consumers migrating to React Router, the vitePlugin and cloudflareDevProxyVitePlugin exports have been renamed and moved (#11904)
-import {
- vitePlugin as remix,
- cloudflareDevProxyVitePlugin,
-} from "@remix/dev";
+import { reactRouter } from "@react-router/dev/vite";
+import { cloudflareDevProxy } from "@react-router/dev/vite/cloudflare";
Removed manifest option
For Remix consumers migrating to React Router, the Vite plugin's manifest option has been removed. The manifest option been superseded by the more powerful buildEnd hook since it's passed the buildManifest argument. You can still write the build manifest to disk if needed, but you'll most likely find it more convenient to write any logic depending on the build manifest within the buildEnd hook itself. (#11573)
If you were using the manifest option, you can replace it with a buildEnd hook that writes the manifest to disk like this:
// react-router.config.ts
import { type Config } from "@react-router/dev/config";
import { writeFile } from "node:fs/promises";
export default {
async buildEnd({ buildManifest }) {
await writeFile(
"build/manifest.json",
JSON.stringify(buildManifest, null, 2),
"utf-8"
);
},
} satisfies Config;
Because React 19 will have first-class support for handling promises in the render pass (via React.use and useAction), we are now comfortable exposing the promises for the APIs that previously returned undefined:
useNavigate()useSubmit()useFetcher().loaduseFetcher().submituseRevalidator().revalidate()routes.tsWhen using the React Router Vite plugin, routes are defined in app/routes.ts. Route config is exported via the routes export, conforming to the RouteConfig type. Route helper functions route, index, and layout are provided to make declarative type-safe route definitions easier.
// app/routes.ts
import {
type RouteConfig,
route,
index,
layout,
} from "@react-router/dev/routes";
export const routes: RouteConfig = [
index("./home.tsx"),
route("about", "./about.tsx"),
layout("./auth/layout.tsx", [
route("login", "./auth/login.tsx"),
route("register", "./auth/register.tsx"),
]),
route("concerts", [
index("./concerts/home.tsx"),
route(":city", "./concerts/city.tsx"),
route("trending", "./concerts/trending.tsx"),
]),
];
For Remix consumers migrating to React Router, you can still configure file system routing within routes.ts using the @react-router/fs-routes package. A minimal route config that reproduces the default Remix setup looks like this:
// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";
export const routes: RouteConfig = flatRoutes();
If you want to migrate from file system routing to config-based routes, you can mix and match approaches by spreading the results of the async flatRoutes function into the array of config-based routes.
// app/routes.ts
import { type RouteConfig, route } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";
export const routes: RouteConfig = [
// Example config-based route:
route("/hello", "./routes/hello.tsx"),
// File system routes scoped to a different directory:
...(await flatRoutes({
rootDirectory: "fs-routes",
})),
];
If you were using Remix's routes option to use alternative file system routing conventions, you can adapt these to the new RouteConfig format using @react-router/remix-config-routes-adapter.
For example, if you were using Remix v1 route conventions in Remix v2, you can combine @react-router/remix-config-routes-adapter with @remix-run/v1-route-convention to adapt this to React Router:
// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { remixConfigRoutes } from "@react-router/remix-config-routes-adapter";
import { createRoutesFromFolders } from "@remix-run/v1-route-convention";
export const routes: RouteConfig = remixConfigRoutes(async (defineRoutes) => {
return createRoutesFromFolders(defineRoutes, {
ignoredFilePatterns: ["**/.*", "**/*.css"],
});
});
Also note that, if you were using Remix's routes option to define config-based routes, you can also adapt these to the new RouteConfig format using @react-router/remix-config-routes-adapter with minimal code changes. While this makes for a fast migration path, we recommend migrating any config-based routes from Remix to the new RouteConfig format since it's a fairly straightforward migration.
// app/routes.ts
-import { type RouteConfig } from "@react-router/dev/routes";
+import { type RouteConfig, route } from "@react-router/dev/routes";
-import { remixConfigRoutes } from "@react-router/remix-config-routes-adapter";
-export const routes: RouteConfig = remixConfigRoutes(async (defineRoutes) => {
- defineRoutes((route) => {
- route("/parent", "./routes/parent.tsx", () => [
- route("/child", "./routes/child.tsx"),
- ]);
- });
-});
+export const routes: RouteConfig = [
+ route("/parent", "./routes/parent.tsx", [
+ route("/child", "./routes/child.tsx"),
+ ]),
+];
React Router now generates types for each of your route modules and passes typed props to route module component exports (#11961, #12019). You can access those types by importing them from ./+types/<route filename without extension>.
See How To > Route Module Type Safety and Explanations > Type Safety for more details.
React Router v7 includes a new prerender config in the vite plugin to support SSG use-cases. This will pre-render your .html and .data files at build time and so you can serve them statically at runtime from a running server or a CDN (#11539)
export default defineConfig({
plugins: [
reactRouter({
async prerender({ getStaticPaths }) {
let slugs = await fakeGetSlugsFromCms();
return [
...getStaticPaths(),
...slugs.map((slug) => `/product/${slug}`),
];
},
}),
tsconfigPaths(),
],
});
async function fakeGetSlugsFromCms() {
await new Promise((r) => setTimeout(r, 1000));
return ["shirt", "hat"];
}
react-router)defer implementation in favor of using raw promises via single fetch and turbo-stream (#11744)
deferAbortedDeferredErrortype TypedDeferredDataUNSAFE_DeferredDataUNSAFE_DEFERRED_SYMBOLreact-router(#11505)
@remix-run/routerreact-router-dom@remix-run/server-runtime@remix-run/testingreact-router-dom package is maintained to ease adoption but it simply re-exports all APIs from react-routerfuture.v7_startTransition flag (#11696)future.v7_normalizeFormMethod future flag (#11697)@remix-run/router
AgnosticDataIndexRouteObjectAgnosticDataNonIndexRouteObjectAgnosticDataRouteMatchAgnosticDataRouteObjectAgnosticIndexRouteObjectAgnosticNonIndexRouteObjectAgnosticRouteMatchAgnosticRouteObjectTrackedPromiseunstable_AgnosticPatchRoutesOnMissFunctionAction -> exported as NavigationType via react-routerRouter exported as RemixRouter to differentiate from RR's <Router>getToPathname (@private)joinPaths (@private)normalizePathname (@private)resolveTo (@private)stripBasename (@private)createBrowserHistory -> in favor of createBrowserRoutercreateHashHistory -> in favor of createHashRoutercreateMemoryHistory -> in favor of createMemoryRoutercreateRoutercreateStaticHandler -> in favor of wrapper createStaticHandler in RR DomgetStaticContextFromErrorreact-router
HashPathnameSearchfuture.v7_prependBasename from the internalized @remix-run/router package (#11726)future.v7_throwAbortReason from internalized @remix-run/router package (#11728)exports field to all packages (#11675)RemixContext to FrameworkContext (#11705)PrefetchPageDescriptor replaced by PageLinkDescriptor (#11960)future.v7_partialHydration flag (#11725)
<RouterProvider fallbackElement> prop
fallbackElement to a hydrateFallbackElement/HydrateFallback on your root routefuture.v7_partialHydration (when using fallbackElement), state.navigation was populated during the initial loadfuture.v7_partialHydration, state.navigation remains in an "idle" state during the initial loadfuture.v7_relativeSplatPath future flag (#11695)v7_skipActionErrorRevalidationv3_fetcherPersist, v3_relativeSplatPath, v3_throwAbortReasoncreateRemixStub to createRoutesStub (#11692)@remix-run/router deprecated detectErrorBoundary option in favor of mapRouteProperties (#11751)react-router/dom subpath export to properly enable react-dom as an optional peerDependency (#11851)
import ReactDOM from "react-dom" in <RouterProvider> in order to access ReactDOM.flushSync(), since that would break createMemoryRouter use cases in non-DOM environmentsreact-router/dom to get the proper component that makes ReactDOM.flushSync() available:
entry.client.tsx:
import { HydratedRouter } from 'react-router/dom'createBrowserRouter/createHashRouter:
import { RouterProvider } from "react-router/dom"future.v7_fetcherPersist flag (#11731)undefined from loaders and actions (#11680, #12057)createRemixRouter/RouterProvider in entry.client instead of RemixBrowser (#11469)json utility (#12146)
Response.json if you still need to construct JSON responses in your app@react-router/*)future.v3_singleFetch flag (#11522)installGlobals() as this should no longer be necessaryexports field to all packages (#11675)react-router through different runtime/adapter packages (#11702)crypto global from the Web Crypto API is now required when using cookie and session APIs
react-router rather than platform-specific packages: (#11837)
createCookiecreateCookieSessionStoragecreateMemorySessionStoragecreateSessionStorageinstallGlobals function from @remix-run/node has been updated to define globalThis.crypto, using Node's require('node:crypto').webcrypto implementationcreateCookieFactorycreateSessionStorageFactorycreateCookieSessionStorageFactorycreateMemorySessionStorageFactory@remix-run/router, @remix-run/server-runtime, and @remix-run/react now that they all live in react-router (#12177)
LoaderFunction, LoaderFunctionArgs, ActionFunction, ActionFunctionArgs, DataFunctionArgs, RouteManifest, LinksFunction, Route, EntryRouteRouteManifest type used by the "remix" code is now slightly stricter because it is using the former @remix-run/router RouteManifest
Record<string, Route> -> Record<string, Route | undefined>AppData type in favor of inlining unknown in the few locations it was usedServerRuntimeMeta* types in favor of the Meta* types they were duplicated fromRoute.* typesRoute.* typesuseFetcher previously had an optional generic (used primarily by Remix v2) that expected the data typetypeof loader/typeof action)useFetcher<LoaderData>()useFetcher<typeof loader>()cookie dependency to ^1.0.1 - please see the release notes for any breaking changes (#12172)@react-router/cloudflare - For Remix consumers migrating to React Router, all exports from @remix-run/cloudflare-pages are now provided for React Router consumers in the @react-router/cloudflare package. There is no longer a separate package for Cloudflare Pages. (#11801)@react-router/cloudflare - The @remix-run/cloudflare-workers package has been deprecated. Remix consumers migrating to React Router should use the @react-router/cloudflare package directly. For guidance on how to use @react-router/cloudflare within a Cloudflare Workers context, refer to the Cloudflare Workers template. (#11801)@react-router/dev - For Remix consumers migrating to React Router, the vitePlugin and cloudflareDevProxyVitePlugin exports have been renamed and moved. (#11904)@react-router/dev - For Remix consumers migrating to React Router who used the Vite plugin's buildEnd hook, the resolved reactRouterConfig object no longer contains a publicPath property since this belongs to Vite, not React Router (#11575)@react-router/dev - For Remix consumers migrating to React Router, the Vite plugin's manifest option has been removed (#11573)@react-router/dev - Update default isbot version to v5 and drop support for isbot@3 (#11770)
isbot@4 or isbot@5 in your package.json:
isbot@3 in your package.json and you have your own entry.server.tsx file in your repo
isbot@5 independent of the React Router v7 upgradeisbot@3 in your package.json and you do not have your own entry.server.tsx file in your repo
isbot@5 in your package.json@react-router/dev - For Remix consumers migrating to React Router, Vite manifests (i.e. .vite/manifest.json) are now written within each build subdirectory, e.g. build/client/.vite/manifest.json and build/server/.vite/manifest.json instead of build/.vite/client-manifest.json and build/.vite/server-manifest.json. This means that the build output is now much closer to what you'd expect from a typical Vite project. (#11573)
build/.vite directory to avoid accidentally serving them in production, particularly from the client build. This was later improved with additional logic that deleted these Vite manifest files at the end of the build process unless Vite's build.manifest had been enabled within the app's Vite config. This greatly reduced the risk of accidentally serving the Vite manifests in production since they're only present when explicitly asked for. As a result, we can now assume that consumers will know that they need to manage these additional files themselves, and React Router can safely generate a more standard Vite build output.react-router - Params, loader data, and action data as props for route component exports (#11961)react-router - Add route module type generation (#12019)react-router - Remove duplicate RouterProvider implementations (#11679)react-router - Stabilize unstable_dataStrategy (#11969)react-router - Stabilize unstable_patchRoutesOnNavigation (#11970)react-router - Add prefetching support to Link/NavLink when using Remix SSR (#11402)react-router - Enhance ScrollRestoration so it can restore properly on an SSR'd document load (#11401)@react-router/dev - Add support for the prerender config in the React Router vite plugin, to support existing SSG use-cases (#11539)@react-router/dev - Remove internal entry.server.spa.tsx implementation which was not compatible with the Single Fetch async hydration approach (#11681)@react-router/serve: Update express.static configurations to support new prerender API (#11547)
build/client/assets folder are served as before, with a 1-year immutable Cache-Control header.html and .data files are not served with a specific Cache-Control header.data files are served with Content-Type: text/x-turbo
express.static, it seems to also add a Cache-Control: public, max-age=0 to .data filessubstr with substring (#12080)react-router - Fix redirects returned from loaders/actions using data() (#12021)@react-router/dev - Enable prerendering for resource routes (#12200)@react-router/dev - resolve config directory relative to flat output file structure (#12187)react-router@react-router/architect@react-router/cloudflare@react-router/dev@react-router/express@react-router/fs-routes@react-router/node@react-router/remix-config-routes-adapter@react-router/serveFull Changelog: v6.28.0...v7.0.0
One column per quarter.
Remove the original defer implementation in favor of using raw promises via single fetch and turbo-stream. This removes these exports from React Router: (#11744)
deferAbortedDeferredErrortype TypedDeferredDataUNSAFE_DeferredDataUNSAFE_DEFERRED_SYMBOL,@remix-run/router into react-router (#11505)react-router-dom into react-router@remix-run/server-runtime into react-router@remix-run/testing into react-routerRemove single fetch future flag. (#11522)
Drop support for Node 16, React Router SSR now requires Node 18 or higher (#11391)
Remove future.v7_startTransition flag (#11696)
useNavigate()useSubmituseFetcher().loaduseFetcher().submituseRevalidator.revalidateRemove future.v7_normalizeFormMethod future flag (#11697)
For Remix consumers migrating to React Router, the crypto global from the Web Crypto API is now required when using cookie and session APIs. This means that the following APIs are provided from react-router rather than platform-specific packages: (#11837)
createCookiecreateCookieSessionStoragecreateMemorySessionStoragecreateSessionStorageFor consumers running older versions of Node, the installGlobals function from @remix-run/node has been updated to define globalThis.crypto, using Node's require('node:crypto').webcrypto implementation.
Since platform-specific packages no longer need to implement this API, the following low-level APIs have been removed:
createCookieFactorycreateSessionStorageFactorycreateCookieSessionStorageFactorycreateMemorySessionStorageFactoryImports/Exports cleanup (#11840)
@remix-run/router
AgnosticDataIndexRouteObjectAgnosticDataNonIndexRouteObjectAgnosticDataRouteMatchAgnosticDataRouteObjectAgnosticIndexRouteObjectAgnosticNonIndexRouteObjectAgnosticRouteMatchAgnosticRouteObjectTrackedPromiseunstable_AgnosticPatchRoutesOnMissFunctionAction -> exported as NavigationType via react-routerRouter exported as DataRouter to differentiate from RR's <Router>getToPathname (@private)joinPaths (@private)normalizePathname (@private)resolveTo (@private)stripBasename (@private)createBrowserHistory -> in favor of createBrowserRoutercreateHashHistory -> in favor of createHashRoutercreateMemoryHistory -> in favor of createMemoryRoutercreateRoutercreateStaticHandler -> in favor of wrapper createStaticHandler in RR DomgetStaticContextFromErrorreact-router
HashPathnameSearchupdate minimum node version to 18 (#11690)
Remove future.v7_prependBasename from the ionternalized @remix-run/router package (#11726)
Migrate Remix type generics to React Router (#12180)
Route.* typesRoute.* typesuseFetcher previously had an optional generic (used primarily by Remix v2) that expected the data typetypeof loader/typeof action)useFetcher<LoaderData>()useFetcher<typeof loader>()Remove future.v7_throwAbortReason from internalized @remix-run/router package (#11728)
Add exports field to all packages (#11675)
node package no longer re-exports from react-router (#11702)
renamed RemixContext to FrameworkContext (#11705)
updates the minimum React version to 18 (#11689)
PrefetchPageDescriptor replaced by PageLinkDescriptor (#11960)
@remix-run/router, @remix-run/server-runtime, and @remix-run/react now that they all live in react-router (#12177)
LoaderFunction, LoaderFunctionArgs, ActionFunction, ActionFunctionArgs, DataFunctionArgs, RouteManifest, LinksFunction, Route, EntryRouteRouteManifest type used by the "remix" code is now slightly stricter because it is using the former @remix-run/router RouteManifest
Record<string, Route> -> Record<string, Route | undefined>AppData type in favor of inlining unknown in the few locations it was usedServerRuntimeMeta* types in favor of the Meta* types they were duplicated fromfuture.v7_partialHydration flag (#11725)
<RouterProvider fallbackElement> prop
fallbackElement to a hydrateFallbackElement/HydrateFallback on your root routefuture.v7_partialHydration (when using fallbackElement), state.navigation was populated during the initial loadfuture.v7_partialHydration, state.navigation remains in an "idle" state during the initial loadRemove v7_relativeSplatPath future flag (#11695)
Drop support for Node 18, update minimum Node vestion to 20 (#12171)
installGlobals() as this should no longer be necessaryRemove remaining future flags (#11820)
v7_skipActionErrorRevalidationv3_fetcherPersist, v3_relativeSplatPath, v3_throwAbortReasonrename createRemixStub to createRoutesStub (#11692)
Remove @remix-run/router deprecated detectErrorBoundary option in favor of mapRouteProperties (#11751)
Add react-router/dom subpath export to properly enable react-dom as an optional peerDependency (#11851)
import ReactDOM from "react-dom" in <RouterProvider> in order to access ReactDOM.flushSync(), since that would break createMemoryRouter use cases in non-DOM environmentsreact-router/dom to get the proper component that makes ReactDOM.flushSync() available:
entry.client.tsx:
import { HydratedRouter } from 'react-router/dom'createBrowserRouter/createHashRouter:
import { RouterProvider } from "react-router/dom"Remove future.v7_fetcherPersist flag (#11731)
Update cookie dependency to ^1.0.1 - please see the release notes for any breaking changes (#12172)
prerender config in the React Router vite plugin, to support existing SSG use-cases (#11539)
prerender config to pre-render your .html and .data files at build time and then serve them statically at runtime (either from a running server or a CDN)prerender can either be an array of string paths, or a function (sync or async) that returns an array of strings so that you can dynamically generate the paths by talking to your CMS, etc.// react-router.config.ts
import type { Config } from "@react-router/dev/config";
export default {
async prerender() {
let slugs = await fakeGetSlugsFromCms();
// Prerender these paths into `.html` files at build time, and `.data`
// files if they have loaders
return ["/", "/about", ...slugs.map((slug) => `/product/${slug}`)];
},
} satisfies Config;
async function fakeGetSlugsFromCms() {
await new Promise((r) => setTimeout(r, 1000));
return ["shirt", "hat"];
}
Params, loader data, and action data as props for route component exports (#11961)
export default function Component({ params, loaderData, actionData }) {}
export function HydrateFallback({ params }) {}
export function ErrorBoundary({ params, loaderData, actionData }) {}
Remove duplicate RouterProvider impliementations (#11679)
React Router now generates types for each of your route modules.
You can access those types by importing them from ./+types.<route filename without extension>.
For example:
// app/routes/product.tsx
import type * as Route from "./+types.product";
export function loader({ params }: Route.LoaderArgs) {}
export default function Component({ loaderData }: Route.ComponentProps) {}
This initial implementation targets type inference for:
Params : Path parameters from your routing config in routes.ts including file-based routingLoaderData : Loader data from loader and/or clientLoader within your route moduleActionData : Action data from action and/or clientAction within your route moduleIn the future, we plan to add types for the rest of the route module exports: meta, links, headers, shouldRevalidate, etc.
We also plan to generate types for typesafe Links:
<Link to="/products/:id" params={{ id: 1 }} />
// ^^^^^^^^^^^^^ ^^^^^^^^^
// typesafe `to` and `params` based on the available routes in your app
Check out our docs for more:
Stabilize unstable_dataStrategy (#11969)
Stabilize unstable_patchRoutesOnNavigation (#11970)
No changes (506329c4e)
chore: re-enable development warnings through a development exports condition. (#12269)
Remove unstable upload handler. (#12015)
Remove unneeded dependency on @web3-storage/multipart-parser (#12274)
Fix redirects returned from loaders/actions using data() (#12021)
fix(react-router): (v7) fix static prerender of non-ascii characters (#12161)
Replace substr with substring (#12080)
Remove the deprecated json utility (#12146)
Response.json if you still need to construct JSON responses in your appRemove unneeded dependency on source-map (#12275)
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/v6/CHANGELOG.md#v6306
See the changelog for release notes: https://github.com/remix-run/react-router/blob/v6/CHANGELOG.md#v6306
Date: 2026-08-18
@remix-run/router - Fix double slash normalization for useNavigate colon urls (3ed85d5)
navigate() is only intended for navigations within the React Router application and not for external navigations to other domainsnavigate for external navigationsFull Changelog: v6.30.5...v6.30.6
⚠️ Don't use version 6.30.5, the publish wasn't complete and the updated @remix-run/router package didn't get published properly. Use 6.30.6.
Date: 2026-08-18
⚠️ Don't use version 6.30.5, the publish wasn't complete and the updated @remix-run/router package didn't get published properly. Use 6.30.6.
react-router - Fix double slash normalization for useNavigate colon urls (#15410)
navigate() is only intended for navigations within the React Router application and not for external navigations to other domainsnavigate for external navigationsFull Changelog: v6.30.4...v6.30.5
See the changelog for release notes: https://github.com/remix-run/react-router/blob/v6/CHANGELOG.md#v6304
See the changelog for release notes: https://github.com/remix-run/react-router/blob/v6/CHANGELOG.md#v6304
Date: 2026-05-29
@remix-run/router - Lift decodePath call out of hot path during route matching (#15119)@remix-run/router - Normalize double slashes in redirect paths (#15118)Full Changelog: v6.30.3...v6.30.4
See the changelog for release notes: https://github.com/remix-run/react-router/blob/v6/CHANGELOG.md#v6303
See the changelog for release notes: https://github.com/remix-run/react-router/blob/v6/CHANGELOG.md#v6303
Date: 2026-01-07
This release addresses 1 security vulnerability:
Full Changelog: v6.30.2...v6.30.3
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/v6/CHANGELOG.md#v6302
See the changelog for release notes: https://github.com/remix-run/react-router/blob/v6/CHANGELOG.md#v6302
Date: 2025-11-13
resolvePath (#14537)Full Changelog: v6.30.1...v6.30.2
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6301
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6301
Date: 2025-05-20
6.29.0 to reduce calls to matchRoutes because it surfaced other issues (#13623)v7_relativeSplatPath is set to false (#13502)Full Changelog: v6.30.0...v6.30.1
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6300
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6300
Date: 2025-02-27
fetcherKey as a parameter to patchRoutesOnNavigation (#13109)6.29.0 via #12169 that caused issues navigating to hash routes inside splat routes for applications using Lazy Route Discovery (patchRoutesOnNavigation) (#13108)Full Changelog: v6.29.0...v6.30.0
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6290
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6290
Date: 2025-01-30
signal as a parameter to patchRoutesOnNavigation (#12900)
data() result (#12845)matchRoutes calls when possible (#12169)patchRoutesOnNavigation path param for fetcher calls (#12899)Full Changelog: v6.28.2...v6.29.0
Nothing published for this version
Nothing published for this version
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6282
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6282
Date: 2025-01-16
key usage when not opted into future.v7_fetcherPersist (#12674)Full Changelog: v6.28.1...v6.28.2
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6281
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6281
Date: 2024-12-20
false (#12441)Full Changelog: v6.28.0...v6.28.1
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6280
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6280
json/defer in favor of returning raw objects
@remix-run/router@1.21.0Date: 2024-11-06
json/defer in favor of returning raw objects
Full Changelog: v6.27.0...v6.28.0
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6270
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6270
unstable_patchRoutesOnNavigation (#11973)
PatchRoutesOnNavigationFunctionArgs type for convenience (#11967)unstable_dataStrategy (#11974)unstable_flushSync option for navigations and fetchers (#11989)unstable_viewTransition option for navigations and the corresponding unstable_useViewTransitionState hook (#11989)Fix bug when submitting to the current contextual route (parent route with an index child) when an ?index param already exists from a prior submission (#12003)
Fix useFormAction bug - when removing ?index param it would not keep other non-Remix index params (#12003)
Fix types for RouteObject within PatchRoutesOnNavigationFunction's patch method so it doesn't expect agnostic route objects passed to patch (#11967)
Updated dependencies:
@remix-run/router@1.20.0Date: 2024-10-11
This release stabilizes a handful of "unstable" APIs in preparation for the pending React Router v7 release (see these posts for more info):
unstable_dataStrategy → dataStrategy (createBrowserRouter and friends) (Docs)unstable_patchRoutesOnNavigation → patchRoutesOnNavigation (createBrowserRouter and friends) (Docs)unstable_flushSync → flushSync (useSubmit, fetcher.load, fetcher.submit) (Docs)unstable_viewTransition → viewTransition (<Link>, <Form>, useNavigate, useSubmit) (Docs)unstable_flushSync option for navigations and fetchers (#11989)unstable_viewTransition option for navigations and the corresponding unstable_useViewTransitionState hook (#11989)unstable_dataStrategy (#11974)unstable_patchRoutesOnNavigation (#11973)
PatchRoutesOnNavigationFunctionArgs type for convenience (#11967)?index param already exists from a prior submission (#12003)useFormAction bug - when removing ?index param it would not keep other non-Remix index params (#12003)preventScrollReset through redirects during concurrent fetches (#11999)console.error on fetcher abort due to back-to-back revalidation calls (#12050)partialHydration when hydrating with errors (#12070)patchRoutesOnNavigation calls (#12055)
unstable_ APIpatchRoutesOnNavigation internally so that multiple navigations with the same start/end would only execute the function once and use the same promisepatch short circuiting if a navigation was interrupted (and the request.signal aborted) since the first invocation's patch would no-opimport() for async routes will already be cached automatically - and if not it's easy enough for users to implement this cache in userlanddiscoveredRoutes FIFO queue from unstable_patchRoutesOnNavigation (#11977)
unstable_ APIpatchRoutesOnNavigationRouteObject within PatchRoutesOnNavigationFunction's patch method so it doesn't expect agnostic route objects passed to patch (#11967)patchRoutesOnNavigation directly to useRouteError instead of wrapping them in a 400 ErrorResponse instance (#12111)Full Changelog: v6.26.2...v6.27.0
Nothing published for this version
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6262
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6262
@remix-run/router@1.19.2Date: 2024-09-09
unstable_dataStrategy API to allow for more advanced implementations (#11943)
unstable_dataStrategy, please review carefully as this includes breaking changes to this APIunstable_HandlerResult to unstable_DataStrategyResultunstable_dataStrategy from a parallel array of unstable_DataStrategyResult[] (parallel to matches) to a key/value object of routeId => unstable_DataStrategyResult
match.shouldLoad)handlerOverride instead of returning a DataStrategyResult
handlerOverride will be wrapped up into a DataStrategyResult and returned fromm match.resolvematch.resolve() into a final results object you should not need to think about the DataStrategyResult typehandlerOverride, then you will need to assign a DataStrategyResult as the value so React Router knows if it's a successful execution or an error (see examples in the documentation for details)fetcherKey parameter to unstable_dataStrategy to allow differentiation from navigational and fetcher callsblocker.proceed is called quickly/synchronously (#11930)Full Changelog: v6.26.1...v6.26.2
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6261
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6261
unstable_patchRoutesOnMiss to unstable_patchRoutesOnNavigation to match new behavior (#11888)@remix-run/router@1.19.1Date: 2024-08-15
unstable_patchRoutesOnMiss to unstable_patchRoutesOnNavigation to match new behavior (#11888)unstable_patchRoutesOnNavigation logic so that we call the method when we match routes with dynamic param or splat segments in case there exists a higher-scoring static route that we've not yet discovered (#11883)
unstable_patchRoutesOnNavigation against so that we don't re-call on subsequent navigations to the same pathFull Changelog: v6.26.0...v6.26.1
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6260
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6260
replace(url, init?) alternative to redirect(url, init?) that performs a history.replaceState instead of a history.pushState on client-side navigation redirects (#11811)future.v7_partialHydration along with unstable_patchRoutesOnMiss (#11838)
router.state.matches will now include any partial matches so that we can render ancestor HydrateFallback components@remix-run/router@1.19.0Date: 2024-08-01
replace(url, init?) alternative to redirect(url, init?) that performs a history.replaceState instead of a history.pushState on client-side navigation redirects (#11811)unstable_data() API for usage with Remix Single Fetch (#11836)
createStaticHandler.query() to allow loaders/actions to return arbitrary data along with custom status/headers without forcing the serialization of data into a Response instanceunstable_dataStrategy such as serializing via turbo-stream in Remix Single Fetchstatus field from HandlerResult
status from unstable_dataStrategy you should instead do so via unstable_data()future.v7_partialHydration along with unstable_patchRoutesOnMiss (#11838)
router.state.matches will now include any partial matches so that we can render ancestor HydrateFallback componentsFull Changelog: v6.25.1...v6.26.0
Nothing published for this version
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6251
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6251
No significant changes to this package were made in this release. See the repo CHANGELOG.md for an overview of all changes in v6.25.1.
Date: 2024-07-17
RouterProvider internals to reduce unnecessary re-renders (#11803)Full Changelog: v6.25.0...v6.25.1
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6250
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6250
future.unstable_skipActionErrorRevalidation as future.v7_skipActionErrorRevalidation (#11769)
Response with a 4xx/5xx status codeshouldRevalidateshouldRevalidate's unstable_actionStatus parameter to actionStatususeMatch so matches/params reflect decoded params (#11789)@remix-run/router@1.18.0Date: 2024-07-16
v7_skipActionErrorRevalidationThis release stabilizes the future.unstable_skipActionErrorRevalidation flag into future.v7_skipActionErrorRevalidation in preparation for the upcoming React Router v7 release.
4xx/5xx Response will not trigger a revalidation by defaultshouldRevalidate's unstable_actionStatus parameter to actionStatusfuture.unstable_skipActionErrorRevalidation as future.v7_skipActionErrorRevalidation (#11769)useMatch so matches/params reflect decoded params (#11789)unstable_patchRoutesOnMiss (#11786)unstable_patchRoutesOnMiss that matched a splat route on the server (#11790)Full Changelog: v6.24.1...v6.25.0
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6241
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6241
future.v7_relativeSplatPath, properly resolve relative paths in splat routes that are children of pathless routes (#11633)@remix-run/router@1.17.1Date: 2024-07-03
polyfill.io reference from warning message because the domain was sold and has since been determined to serve malware (#11741)
NavLinkRenderProps type for easier typing of custom NavLink callback (#11553)future.v7_relativeSplatPath, properly resolve relative paths in splat routes that are children of pathless routes (#11633)router.routes identity/reflow during route patching (#11740)Full Changelog: v6.24.0...v6.24.1
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6240
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6240
unstable_patchRoutesOnMiss docs: https://reactrouter.com/v6/routers/create-browser-router@remix-run/router@1.17.0Date: 2024-06-24
We're really excited to release our new API for "Lazy Route Discovery" in v6.24.0! For some background information, please check out the original RFC. The tl;dr; is that ever since we introduced the Data APIs in v6.4 via <RouterProvider>, we've been a little bummed that one of the tradeoffs was the lack of a compelling code-splitting story mirroring what we had in the <BrowserRouter>/<Routes> apps. We took a baby-step towards improving that story with route.lazy in v6.9.0, but with v6.24.0 we've gone the rest of the way.
With "Fog of War", you can now load portions of the route tree lazily via the new unstable_patchRoutesOnMiss option passed to createBrowserRouter (and it's memory/hash counterparts). This gives you a way to hook into spots where React Router is unable to match a given path and patch new routes into the route tree during the navigation (or fetcher call).
Here's a very small example, but please refer to the documentation for more information and use cases:
const router = createBrowserRouter(
[
{
id: "root",
path: "/",
Component: RootComponent,
},
],
{
async unstable_patchRoutesOnMiss({ path, patch }) {
if (path === "/a") {
// Load the `a` route (`{ path: 'a', Component: A }`)
let route = await getARoute();
// Patch the `a` route in as a new child of the `root` route
patch("root", [route]);
}
},
}
);
fetcher.submit types - remove incorrect navigate/fetcherKey/unstable_viewTransition options because they are only relevant for useSubmit (#11631)location.state values passed to <StaticRouter> (#11495)Full Changelog: v6.23.1...v6.24.0
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6231
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6231
<Await> (#11513)@remix-run/router@1.16.1Date: 2024-05-10
undefined to be resolved through <Await> (#11513)document check when checking for document.startViewTransition availability (#11544)react-router-dom/server import back to react-router-dom instead of index.ts (#11514)@remix-run/router - Support unstable_dataStrategy on staticHandler.queryRoute (#11515)Full Changelog: v6.23.0...v6.23.1
Nothing published for this version
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6230
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6230
unstable_dataStrategy configuration option (#11098)
@remix-run/router@1.16.0Date: 2024-04-23
The new unstable_dataStrategy API is a low-level API designed for advanced use-cases where you need to take control over the data strategy for your loader/action functions. The default implementation is today's behavior, to fetch all loaders in parallel, but this option allows users to implement more advanced data flows including Remix "Single Fetch", user-land middleware/context APIs, automatic loader caching, and more. Please see the docs for more information.
Note: This is a low-level API intended for advanced use-cases. This overrides React Router's internal handling of loader/action execution, and if done incorrectly will break your app code. Please use with caution and perform the appropriate testing.
Currently, all active loader's revalidate after any action submission, regardless of the action result. However, in the majority of cases a 4xx/5xx response from an action means that no data was actually changed and the revalidation is unnecessary. We've introduced a new future.unstable_skipActionErrorRevalidation flag that changes the behavior here, and we plan to make this the default in future version of React Router.
With this flag enabled, action's that return/throw a 4xx/5xx response status will no longer automatically revalidate. If you need to revalidate after a 4xx/5xx result with this flag enabled, you can still do that via returning true from shouldRevalidate - which now also receives a new unstable_actionStatus argument alongside actionResult so you can make decision based on the status of the action response without having to encode it into the action data.
unstable_dataStrategy configuration option (#11098, #11377)@remix-run/router - Add a new future.unstable_skipActionRevalidation future flag (#11098)@remix-run/router - SSR: Added a new skipLoaderErrorBubbling options to the staticHandler.query method to disable error bubbling by the static handler for use in Remix's Single Fetch implementation (#11098, (#11377))Full Changelog: v6.22.3...v6.23.0
Nothing published for this version
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6223
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6223
@remix-run/router@1.15.3Date: 2024-03-07
future.v7_partialHydration bug that would re-run loaders below the boundary on hydration if SSR loader errors bubbled to a parent boundary (#11324)future.v7_partialHydration bug that would consider the router uninitialized if a route did not have a loader (#11325)Full Changelog: v6.22.2...v6.22.3
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6222
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6222
@remix-run/router@1.15.2Date: 2024-02-28
Full Changelog: v6.22.1...v6.22.2
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6221
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6221
@remix-run/router@1.15.1Date: 2024-02-16
Full Changelog: v6.22.0...v6.22.1
Nothing published for this version
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6220
See the changelog for release notes: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6220
@remix-run/router@1.15.0Date: 2024-02-01
In 2021, the HTTP Archive launched the Core Web Vitals Technology Report dashboard:
By combining the powers of real-user experiences in the Chrome UX Report 26 (CrUX) dataset with web technology detections in HTTP Archive 30, we can get a glimpse into how architectural decisions like choices of CMS platform or JavaScript framework play a role in sites’ CWV performance.
They use a tool called wappalyzer to identify what technologies a given website is using by looking for certain scripts, global JS variables, or other identifying characteristics. For example, for Remix applications, they look for the global __remixContext variable to identify that a website is using Remix.
It was brought to our attention that React Router was unable to be reliably identified because there are no identifying global aspects. They are currently looking for external scripts with react-router in the name. This will identify sites using React Router from a CDN such as unpkg - but it will miss the vast majority of sites that are installing React Router from the npm registry and bundling it into their JS files. This results in drastically under-reporting the usage of React Router on the web.
Starting with version 6.22.0, sites using react-router-dom will begin adding a window.__reactRouterVersion variable that will be set to a string value of the SemVer major version number (i.e., window.__reactRouterVersion = "6";) so that they can be properly identified.
window.__reactRouterVersion for CWV Report detection (#11222)createStaticHandler future.v7_throwAbortReason flag to throw request.signal.reason (defaults to a DOMException) when a request is aborted instead of an Error such as new Error("query() call aborted: GET /path") (#11104)
DOMException was added in Node v17 so you will not get a DOMException on Node 16 and below.ErrorResponse status code if passed to getStaticContextFormError (#11213)Full Changelog: v6.21.3...v6.22.0
Your coding agent can read these notes before it upgrades. Set up the MCP server →