React Router Releases
May 5, 2026 ยท View on GitHub
This page lists all releases/release notes for React Router back to v6.0.0. For releases prior to v6, please refer to the Github Releases Page.
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
- v7.15.0
- v7.14.2
- v7.14.1
- v7.14.0
- v7.13.2
- v7.13.1
- v7.13.0
- v7.12.0
- v7.11.0
- v7.10.1
- v7.10.0
- v7.9.6
- v7.9.5
- v7.9.4
- v7.9.3
- v7.9.2
- v7.9.1
- v7.9.0
- v7.8.2
- v7.8.1
- v7.8.0
- v7.7.1
- v7.7.0
- v7.6.3
- v7.6.2
- v7.6.1
- v7.6.0
- v7.5.3
- v7.5.2
- v7.5.1
- v7.5.0
- v7.4.1
- v7.4.0
- v7.3.0
- v7.2.0
- v7.1.5
- v7.1.4
- v7.1.3
- v7.1.2
- v7.1.1
- v7.1.0
- v7.0.2
- v7.0.1
- v7.0.0
- React Router v6 Releases
- v6.30.3
- v6.30.2
- v6.30.1
- v6.30.0
- v6.29.0
- v6.28.2
- v6.28.1
- v6.28.0
- v6.27.0
- v6.26.2
- v6.26.1
- v6.26.0
- v6.25.1
- v6.25.0
- v6.24.1
- v6.24.0
- v6.23.1
- v6.23.0
- v6.22.3
- v6.22.2
- v6.22.1
- v6.22.0
- v6.21.3
- v6.21.2
- v6.21.1
- v6.21.0
- v6.20.1
- v6.20.0
- v6.19.0
- v6.18.0
- v6.17.0
- v6.16.0
- v6.15.0
- v6.14.2
- v6.14.1
- v6.14.0
- v6.13.0
- v6.12.1
- v6.12.0
- v6.11.2
- v6.11.1
- v6.11.0
- v6.10.0
- v6.9.0
- v6.8.2
- v6.8.1
- v6.8.0
- v6.7.0
- v6.6.2
- v6.6.1
- v6.6.0
- v6.5.0
- v6.4.5
- v6.4.4
- v6.4.3
- v6.4.2
- v6.4.1
- v6.4.0
- v6.3.0
- v6.2.2
- v6.2.1
- v6.2.0
- v6.1.1
- v6.1.0
- v6.0.2
- v6.0.1
- v6.0.0
v7.15.0
Date: 2026-05-05
What's Changed
Stabilizations
We've stabilized a bunch of APIs in this release in preparation for a React Router v8 release hopefully in the next month or two. These flag/prop renames are breaking changes if you've already opted into the unstable APIs so please make sure you make the appropriate changes if so.
future.unstable_passThroughRequestsโfuture.v8_passThroughRequestsfuture.unstable_subResourceIntegrityโ top-levelconfig.subResourceIntegrityprerender.unstable_concurrencyโprerender.concurrencyunstable_urlโurl(loader, action, middleware, instrumentation args)unstable_instrumentationsโinstrumentations- Plus associated types (
ServerInstrumentation,ClientInstrumentation, etc.)
- Plus associated types (
unstable_patternโpattern(loader, action, middleware, instrumentation args)unstable_defaultShouldRevalidateโdefaultShouldRevalidateunstable_useTransitionsโuseTransitionsunstable_maskโmask(on<Link>,useLinkClickHandler,useNavigate, andLocation)
Route matching optimizations
We've added a handful of route matching optimizations in this release for Framework and Data mode. The changes are mostly related to caching the internal flattened/ranked route branches and reducing additional calls to matchRoutes along the critical path. This should result in improved performance during both server-side request handling and client-side navigations.
Minor Changes
react-router- Stabilizeunstable_defaultShouldRevalidateasdefaultShouldRevalidateon<Link>,<Form>,useLinkClickHandler,useSubmit,fetcher.submit, andsetSearchParams(14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
react-router- Stabilize the instrumentation APIs (14999)unstable_instrumentationsis nowinstrumentationsunstable_patternis nowpattern- The
unstable_ServerInstrumentation,unstable_ClientInstrumentation,unstable_InstrumentRequestHandlerFunction,unstable_InstrumentRouterFunction,unstable_InstrumentRouteFunction, andunstable_InstrumentationHandlerResulttypes have had theirunstable_prefixes removed - โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
react-router- Stabilizeunstable_maskasmaskon<Link>,useLinkClickHandler, anduseNavigate, and rename the correspondingLocation.unstable_maskfield toLocation.mask(14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
react-router- Stabilize theunstable_normalizePathoption onstaticHandler.queryandstaticHandler.queryRouteasnormalizePath(14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
react-router- Stabilizefuture.unstable_passThroughRequestsasfuture.v8_passThroughRequests(14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
react-router- Removeunstable_subResourceIntegrityfrom the runtimeFutureConfigtype; the flag is now controlled by the top-levelsubResourceIntegrityoption inreact-router.config.ts(14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
react-router- Stabilizeunstable_urlasurlonloader,action, andmiddlewarefunction args (14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
react-router- Stabilizeunstable_useTransitionsasuseTransitionson<BrowserRouter>,<HashRouter>,<HistoryRouter>,<MemoryRouter>,<Router>,<RouterProvider>,<HydratedRouter>, anduseLinkClickHandler(14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
@react-router/dev- Stabilizefuture.unstable_passThroughRequestsasfuture.v8_passThroughRequests(14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
@react-router/dev- Stabilizeprerender.unstable_concurrencyasprerender.concurrency(14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
@react-router/dev- Stabilizefuture.unstable_subResourceIntegrityas a top-levelsubResourceIntegrityconfig option inreact-router.config.ts(14999)- โ ๏ธ This is a breaking change if you have already opted into the unstable version - you will need to update your code accordingly
Patch Changes
react-router- Addnonceto<Scripts><link rel="modulepreload">elements (if provided) (af5d49b)react-router- Fix a bug withunstable_defaultShouldRevalidate={false}where parent routes that did not export ashouldRevalidatefunction could be incorrectly included in the single fetch call for new child route data (#15012)react-router- Markmaskas an optional field inLocationfor easier mocking in unit tests (#14999)react-router- Improve server-side route matching performance by pre-computing flattened/cached route branches (#14967) (af5d49b)- Performance benchmarks showed roughly a 10-15% improvement in server-side request handling performance
react-router- Cache flattened/ranked route branches to optimize server-side route matching (#14967)react-router- Improve route matching performance in Framework/Data Mode (#14971) (af5d49b)- Avoiding unnecessary calls to
matchRoutesin data router scenarios- This includes adding back the optimization that was removed in
7.6.0(#13562) - The issues that prompted the revert have been addressed by using the available router
matchesbut always updatingmatch.routeto the latest route in themanifest
- This includes adding back the optimization that was removed in
- Leverage pre-computed pre-computing flattened/cached route branches during client side route matching
- Performance benchmarks showed roughly a 15-30% improvement in server-side request handling performance
- Avoiding unnecessary calls to
Full Changelog: v7.14.2...v7.15.0
v7.14.2
Date: 2026-04-21
Patch Changes
-
react-router- Remove the un-documented custom error serialization logic from the internal turbo-stream implementation. React Router only automatically handles serialization ofErrorand it's standard subtypes (SyntaxError,TypeError, etc.). (#14992) -
react-router- Properly handle parent middleware redirects duringfetcher.load(#14974) -
react-router- Remove redundantOmit<RouterProviderProps, "flushSync">fromreact-router/domRouterProvider(#14874) -
react-router- Improved types forgeneratePath'sparamarg (#14984)-
Type errors when required params are omitted:
// Before // Passes type checks, but throws at runtime ๐ฅ generatePath(":required", { required: null }); // After generatePath(":required", { required: null }); // ^^^^^^^^ Type 'null' is not assignable to type 'string'.ts(2322) -
Allow omission of optional params:
// Before generatePath(":optional?", {}); // ^^ Property 'optional' is missing in type '{}' but required in type '{ optional: string | null | undefined; }'.ts(2741) // After generatePath(":optional?", {}); -
Allows extra keys:
// Before generatePath(":a", { a: "1", b: "2" }); // ^ Object literal may only specify known properties, and 'b' does not exist in type '{ a: string; }'.ts(2353) // After generatePath(":a", { a: "1", b: "2" });
-
-
@react-router/dev- Fix typegen for layouts without pages (#14875)- Previously, typegen could produce
pages: ;in.react-router/types/+routes.tswhen a route corresponded to 0 pages - Now,
pages: never;is correctly generated for those cases
- Previously, typegen could produce
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
@react-router/dev- Forunstable_reactRouterRSCVite plugin consumers, require@vitejs/plugin-reactin user Vite config, and more reliably split route modules (#14965)- โ ๏ธ This is a breaking change if you have begun using the
unstable_reactRouterRSCVite plugin - please install@vitejs/plugin-reactand add thereactplugin to your Vite plugins array.
- โ ๏ธ This is a breaking change if you have begun using the
Full Changelog: v7.14.1...v7.14.2
v7.14.1
Date: 2026-04-13
Patch Changes
react-router- Fix a potential race condition that can occur when rendering aHydrateFallbackand initial loaders land before therouter.subscribecall happens in theRouterProviderlayout effect (#14497)react-router- Normalize double-slashes in redirect paths (#14962)@react-router/dev- Add TypeScript 6 support to peer dependency ranges (#14935)
Full Changelog: v7.14.0...v7.14.1
v7.14.0
Date: 2026-04-02
Minor Changes
- Add support for Vite 8 (#14876)
Patch Changes
react-router- Remove recursion from vendoredturbo-streamv2 implementation allowing for encoding/decoding of large payloads (#14838)react-router- FixencodeViaTurboStreammemory leak via unremovedAbortSignallistener (#14900)@react-router/dev- Support for prerendering multiple server bundles withv8_viteEnvironmentApi(#14921)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
@react-router/dev- Pre-rendering and SPA Mode support for RSC Framework Mode (#14907)@react-router/dev- Updatereact-router revealto support RSC Framework Mode forentry.client,entry.rsc,entry.ssr(#14904)react-router- Support<Link prefetch>in RSC Framework Mode (#14902)react-router- Add support for new route module exports in unstable RSC Framework Mode (#14901)-
โ ๏ธ This is a breaking change if you have already adopted RSC Framework Mode in it's unstable state - you will need to update your route modules to export the new annotations
-
The following route module components have their own mutually exclusive server component counterparts:
Client Component export Server Component export defaultServerComponentErrorBoundaryServerErrorBoundaryLayoutServerLayoutHydrateFallbackServerHydrateFallback -
If you were previously exporting a
ServerComponent, yourErrorBoundary,Layout, andHydrateFallbackwere also implicitly server components -
If you want to keep those as server components - rename them and prefix them with
Server -
If you were previously importing the implementations of those components from a client module, you can inline them
// Before import { ErrorBoundary as ClientErrorBoundary } from "./client"; export function ServerComponent() { // ... } export function ErrorBoundary() { return <ClientErrorBoundary />; } export function Layout() { // ... } export function HydrateFallback() { // ... }// After export function ServerComponent() { // ... } export function ErrorBoundary() { // previous implementation of ClientErrorBoundary, this is now a client component } export function ServerLayout() { // rename previous Layout export to ServerLayout to make it a server component } export function ServerHydrateFallback() { // rename previous HydrateFallback export to ServerHydrateFallback to make it a server component }
-
Full Changelog: v7.13.2...v7.14.0
v7.13.2
Date: 2026-03-23
What's Changed
Pass-through Requests (unstable)
By default, React Router normalizes the request.url passed to your loader, action, and middleware functions by removing React Router's internal implementation details (.data suffixes, index + _routes query params). This release introduces a new future.unstable_passThroughRequests flag to disable this normalization and pass the raw HTTP request instance to your handlers.
In addition to reducing server-side overhead by eliminating multiple new Request() calls on the critical path, this also provides additional visibility to your route handlers/instrumentations allowing you to differentiate document from data requests.
If you were previously relying on the normalization of request.url, you can switch to use the new sibling unstable_url parameter which contains a URL instance representing the normalized location:
// โ Before: you could assume there was no `.data` suffix in `request.url`
export async function loader({ request }: Route.LoaderArgs) {
let url = new URL(request.url);
if (url.pathname === "/path") {
// This check will fail with the flag enabled because the `.data` suffix will
// exist on data requests
}
}
// โ
After: use `unstable_url` for normalized routing logic and `request.url`
// for raw routing logic
export async function loader({ request, unstable_url }: Route.LoaderArgs) {
if (unstable_url.pathname === "/path") {
// This will always have the `.data` suffix stripped
}
// And now you can distinguish between document versus data requests
let isDataRequest = new URL(request.url).pathname.endsWith(".data");
}
Route handlers/middleware unstable_url parameter
We have added a new unstable_url: URL parameter to route handler methods (loader, action, middleware, etc.) that contains the normalized URL the application is navigating to or fetching with React Router implementation details removed (.datasuffix, index/_routes query params).
This parameter is primarily needed when adopting the new future.unstable_passthroughRequests future flag as a way to continue accessing the normalized URL. If you don't have the flag enabled, then unstable_url will match request.url.
Patch Changes
react-router- FixclientLoader.hydratewhen an ancestor route is also hydrating aclientLoader(#14835)react-router- Fix type error when passing Framework Mode route components usingRoute.ComponentPropstocreateRoutesStub(#14892)react-router- Fix percent encoding in relative path navigation (#14786)react-router- Internal refactor to consolidate framework-agnostic/React-specific route type layers - no public API changes (#14765)@react-router/dev- Fixreact-router devcrash when Unix socket files exist in the project root (#14854)@react-router/dev- Escape redirect locations in pre-rendered redirect HTML (#14880)create-react-router- replacechalkwithpicocolors(#14837)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Sync protocol validation to RSC flows (#14882)react-router- Addfuture.unstable_passThroughRequestsflag (#14775)react-router- Add a newunstable_url: URLparameter to route handler methods (loader,action,middleware, etc.) representing the normalized URL the application is navigating to or fetching, with React Router implementation details removed (.datasuffix,index/_routesquery params) (#14775)
Full Changelog: v7.13.1...v7.13.2
v7.13.1
Date: 2026-02-23
What's Changed
URL Masking (unstable)
This release includes a new <Link unstable_mask> API which brings first-class support for URL masking to Framework/Data Mode (RFC). This allows the same type of UI you could achieve in Declarative Mode via manual backgroundLocation management. That example has been converted to Data Mode using the new API here.
Patch Changes
react-router- Clear timeout whenturbo-streamencoding completes (#14810)react-router- Improve error message whenOriginheader is invalid (#14743)react-router- FixmatchPathoptional params matching without a"/"separator. (#14689)matchPath("/users/:id?", "/usersblah")now returns nullmatchPath("/test_route/:part?", "/test_route_more")now returns null.
react-router- FixHydrateFallbackrendering during initial lazy route discovery with matching splat route (#14740)react-router- Preserve query parameters and hash on manifest version mismatch reload (#14813)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- RSC: fix null reference exception in bad codepath leading to invalid route tree comparisons (#14780)react-router- RSC: addunstable_getRequestAPI (#14758)react-router- RSC: Update failed origin checks to return a 400 status and appropriate UI instead of a generic 500 (#14755)react-router- Add support for<Link unstable_mask>in Framework/Data Mode which allows users to navigate to a URL in the router but "mask" the URL displayed in the browser (#14716)-
This is useful for contextual routing usages such as displaying an image in a modal on top of a gallery, but displaying a browser URL directly to the image that can be shared and loaded without the contextual gallery in the background
-
The masked location, if present, will be available on
useLocation().unstable_maskso you can detect whether you are currently masked or not -
Masked URLs only work for SPA use cases, and will be removed from
history.stateduring SSR -
This provides a first-class API to mask URLs in Framework/Data Mode to achieve the same behavior you could do in Declarative Mode via manual
backgroundLocationmanagement.// routes/gallery.tsx export function clientLoader({ request }: Route.LoaderArgs) { let sp = new URL(request.url).searchParams; return { images: getImages(), // When the router location has the image param, load the modal data modalImage: sp.has("image") ? getImage(sp.get("image")!) : null, }; } export default function Gallery({ loaderData }: Route.ComponentProps) { return ( <> <GalleryGrid> {loaderData.images.map((image) => ( <Link key={image.id} {/* Navigate the router to /galley?image=N */}} to={`/gallery?image=${image.id}`} {/* But display /images/N in the URL bar */}} unstable_mask={`/images/${image.id}`} > <img src={image.url} alt={image.alt} /> </Link> ))} </GalleryGrid> {/* When the modal data exists, display the modal */} {data.modalImage ? ( <dialog open> <img src={data.modalImage.url} alt={data.modalImage.alt} /> </dialog> ) : null} </> ); }
-
Full Changelog: v7.13.0...v7.13.1
v7.13.0
Date: 2026-01-23
Minor Changes
react-router- AddcrossOriginprop toLinkscomponent (#14687)
Patch Changes
react-router- Fix double slash normalization foruseNavigatepaths with a colon (#14718)react-router- Fix missingnonceon inlinecriticalCss(#14691)react-router- Update failed origin checks to return a 400 status instead of a 500 (#14737)react-router- LoosenallowedActionOriginsglob check so**matches all domains (#14722)@react-router/dev- Bump@remix-run/node-fetch-serverdep (#14704)@react-router/fs-routes- Fix route file paths when routes directory is outside of the app directory (#13937)
Full Changelog: v7.12.0...v7.13.0
v7.12.0
Date: 2026-01-07
Security Notice
This release addresses 3 security vulnerabilities:
- CSRF in React Router Action/Server Action Request Processing
- XSS via Open Redirects
- React Router SSR XSS in ScrollRestoration
Minor Changes
react-router- Add additional layer of CSRF protection by rejecting submissions to UI routes from external origins (#14708)- If you need to permit access to specific external origins, there is a new
allowedActionOriginsconfig field inreact-router.config.tswhere you can specify external origins
- If you need to permit access to specific external origins, there is a new
Patch Changes
react-router- FixgeneratePathwhen used with suffixed params (i.e.,/books/:id.json) (#14269)react-router- Escape HTML in scroll restoration keys (#14705)react-router- Validate redirect locations (#14706)@react-router/dev- FixMaximum call stack size exceedederrors when HMR is triggered against code with cyclic imports (#14522)@react-router/dev- Skip SSR middleware invite previewserver for SPA mode (#14673)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- PreserveclientLoader.hydrate=truewhen using<HydratedRouter unstable_instrumentations>(#14674)react-router- Pass<Scripts nonce>value through to the underlyingimportmapscripttag when usingfuture.unstable_subResourceIntegrity(#14675)react-router- ExportUNSAFE_createMemoryHistoryandUNSAFE_createHashHistoryalongsideUNSAFE_createBrowserHistoryfor consistency (#14663)- These are not intended to be used for new apps but intended to help apps using
unstable_HistoryRoutermigrate from v6->v7 so they can adopt the newer APIs
- These are not intended to be used for new apps but intended to help apps using
@react-router/dev- Add a newfuture.unstable_trailingSlashAwareDataRequestsflag to provide consistent behavior ofrequest.pathnameinsidemiddleware,loader, andactionfunctions on document and data requests when a trailing slash is present in the browser URL. (#14644)-
Currently, your HTTP and
requestpathnames would be as follows for/a/b/cand/a/b/c/URL /a/b/cHTTP pathname requestpathname`Document /a/b/c/a/b/cโData /a/b/c.data/a/b/cโURL /a/b/c/HTTP pathname requestpathname`Document /a/b/c//a/b/c/โData /a/b/c.data/a/b/cโ ๏ธ -
With this flag enabled, these pathnames will be made consistent though a new
_.dataformat for client-side.datarequests:URL /a/b/cHTTP pathname requestpathname`Document /a/b/c/a/b/cโData /a/b/c.data/a/b/cโURL /a/b/c/HTTP pathname requestpathname`Document /a/b/c//a/b/c/โData /a/b/c/_.dataโฌ ๏ธ/a/b/c/โ -
This a bug fix but we are putting it behind an opt-in flag because it has the potential to be a "breaking bug fix" if you are relying on the URL format for any other application or caching logic
-
Enabling this flag also changes the format of client side
.datarequests from/_root.datato/_.datawhen navigating to/to align with the new format - This does not impact therequestpathname which is still/in all cases
-
Full Changelog: v7.11.0...v7.12.0
v7.11.0
Date: 2025-12-17
What's Changed
We've added vite preview support and stabilized the client-side onError API - please make the appropriate changes if you've adopted the unstable_onError API already in a prior release.
vite preview Support
We've added support for vite preview when using Framework mode to make it easy to preview your production build.
Stabilized Client-side onError
The existing <RouterProvider unstable_onError>/<HydratedRouter unstable_onError> APIs have been stabilized as <RouterProvider onError>/<HydratedRouter onError>. Please see the Error Reporting docs for more information.
Call-site Revalidation Opt-out (unstable)
We've added initial unstable support for call-site revalidation opt-out via a new unstable_defaultShouldRevalidate flag (RFC). This flag is available on all navigation/fetcher submission APIs to alter standard revalidation behavior. If any routes include a shouldRevalidate function, then the flag value will be passed to that function so the route has the final say on revalidation behavior.
<Form method="post" unstable_defaultShouldRevalidate={false} />
submit(data, { method: "post", unstable_defaultShouldRevalidate: false })
<fetcher.Form method="post" unstable_defaultShouldRevalidate={false} />
fetcher.submit(data, { method: "post", unstable_defaultShouldRevalidate: false })
This flag is also available on non-submission navigational use cases - for example, you may want to opt-out of revalidation when adding a search param that doesn't impact the UI:
<Link to="?analytics-param=1" unstable_defaultShouldRevalidate={false} />;
navigate("?analytics-param=1", { unstable_defaultShouldRevalidate: false });
setSearchParams(params, { unstable_defaultShouldRevalidate: false });
Minor Changes
react-router- Stabilize<HydratedRouter onError>/<RouterProvider onError>(#14546)@react-router/dev- Addvite previewsupport (#14507)
Patch Changes
react-router- Fixunstable_useTransitionsprop on<Router>component to permit omission for backwards compatibility (#14646)react-router- Allow redirects to be returned from client side middleware (#14598)react-router- HandledataStrategyimplementations that return insufficient result sets by adding errors for routes without any available result (#14627)@react-router/serve- Updatecompressionandmorgandependencies to addresson-headersCVE: GHSA-76c9-3jph-rj3q (#14652)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- RSC: Support for throwingdata()and Response from server component render phase (#14632)- Response body is not serialized as async work is not allowed as error encoding phase.
- If you wish to transmit data to the boundary, throw
data()instead
react-router- RSC: Support for throwingredirectResponse's at render time (#14596)react-router- RSC:routeRSCServerRequestreplacefetchServerwithserverResponse(#14597)@react-router/dev- RSC (Framework mode): Manual chunking forreactandreact-routerdeps (#14655)@react-router/dev- RSC (Framework mode): Optimizereact-server-dom-webpackif in projectpackage.json(#14656)@react-router/{dev,serve}- RSC (Framework mode): Support custom entrypoints (#14643)react-router- Add a newunstable_defaultShouldRevalidateflag to various APIs to allow opt-ing out of standard revalidation behaviors (#14542)
Full Changelog: v7.10.1...v7.11.0
v7.10.1
Date: 2025-12-04
Patch Changes
react-router- Update theuseOptimisticstub we provide for React 18 users to use a stable setter function to avoid potentialuseEffectloops - specifically when using<Link viewTransition>(#14628)@react-router/dev- Import ESM packagepkg-typeswith a dynamicimport()to fix issues on Node 20.18 (#14624)@react-router/dev- Updatevalibotdependency to^1.2.0to address GHSA-vqpr-j7v3-hqw9 (#14608)
Full Changelog: v7.10.0...v7.10.1
v7.10.0
Date: 2025-12-02
What's Changed
We've stabilized a handful of existing APIs and future flags in this release, please make the appropriate changes if you'd adopted any of these APIs in their unstable state!
Stabilized future.v8_splitRouteModules
The existing future.unstable_splitRouteModules flag has been stabilized as future.v8_splitRouteModules in react-router.config.ts. Please see the docs for more information on adopting this flag.
Stabilized future.v8_viteEnvironmentApi
The existing future.unstable_viteEnvironmentApi flag has been stabilized as future.v8_viteEnvironmentApi in react-router.config.ts. Please see the docs for more information on adopting this flag.
Stabilized fetcher.reset()
The existing fetcher.unstable_reset() API has been stabilized as fetcher.reset().
Stabilized DataStrategyMatch.shouldCallHandler()
The existing low-level DataStrategyMatch.unstable_shouldCallHandler()/DataStrategyMatch.unstable_shouldRevalidateArgs APIs have been stabilized as DataStrategyMatch.shouldCallHandler()/DataStrategyMatch.shouldRevalidateArgs. Please see the docs for information about using a custom dataStrategy and how to migrate away from the deprecated DataStrategyMatch.shouldLoad API if you are using that today.
Minor Changes
react-router- Stabilizefetcher.reset()(#14545)- โ ๏ธ This is a breaking change if you have begun using
fetcher.unstable_reset()- please update your code to usefetcher.reset()
- โ ๏ธ This is a breaking change if you have begun using
react-router- Stabilize thedataStrategymatch.shouldCallHandler()/match.shouldRevalidateArgsAPIs (#14592)- The
match.shouldLoadAPI is now marked deprecated in favor of these more powerful alternatives - โ ๏ธ This is a breaking change if you have begun using
match.unstable_shouldCallHandler()/match.unstable_shouldRevalidateArgs- please update your code to usematch.shouldCallHandler()/match.shouldRevalidateArgs
- The
@react-router/dev- Stabilizefuture.v8_splitRouteModules, replacingfuture.unstable_splitRouteModules(#14595)- โ ๏ธ This is a breaking change if you have begun using
future.unstable_splitRouteModules- please update yourreact-router.config.ts
- โ ๏ธ This is a breaking change if you have begun using
@react-router/dev- Stabilizefuture.v8_viteEnvironmentApi, replacingfuture.unstable_viteEnvironmentApi(#14595)- โ ๏ธ This is a breaking change if you have begun using
future.unstable_viteEnvironmentApi- please update yourreact-router.config.ts
- โ ๏ธ This is a breaking change if you have begun using
Patch Changes
react-router- Fix a Framework Mode bug where thedefaultShouldRevalidateparameter toshouldRevalidatewould not be correct afteractionreturned a 4xx/5xx response (truewhen it should have beenfalse) (#14592)- If your
shouldRevalidatefunction relied on that parameter, you may have seen unintended revalidations
- If your
react-router- Fixfetcher.submitfailing with plain objects containing atagNameproperty (#14534)react-router- Fix the promise returned fromuseNavigatein Framework/Data Mode so that it properly tracks the duration ofpopstatenavigations (i.e.,navigate(-1)) (#14524)react-router- PreservestatusTexton theErrorResponseinstance when throwingdata()from a route handler (#14555)react-router- Optimizehref()to avoid backtracking regex on splat (#14329)@react-router/dev- Fix internal type error inuseRoutetypes that surfaces whenskipLibCheckis disabled (#14577)@react-router/dev- Load environment variables before evaluatingroutes.ts(#14446)-
For example, you can now compute your routes based on
VITE_-prefixed environment variables// app/routes.ts import { type RouteConfig, route } from "@react-router/dev/routes"; const routes: RouteConfig = []; // Only add the route when VITE_ENV_ROUTE is set if (import.meta.env.VITE_ENV_ROUTE === "my-route") { routes.push(route("my-route", "routes/my-route.tsx")); } export default routes;
-
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Addunstable_patternto the parameters for client sideunstable_onError(#14573)react-router- Refactor howunstable_onErroris called internally byRouterProviderto avoid potential strict mode issues (#14573)react-router- Add newunstable_useTransitionsflag to routers to give users control over the usage ofReact.startTransitionandReact.useOptimistic(#14524)- Please see the docs for more information
- Framework Mode + Data Mode:
<HydratedRouter unstable_transition>/<RouterProvider unstable_transition>- When left unset (current default behavior)
- Router state updates are wrapped in
React.startTransition - โ ๏ธ This can lead to buggy behaviors if you are wrapping your own navigations/fetchers in
React.startTransition - You should set the flag to
trueif you run into this scenario to get the enhanceduseOptimisticbehavior (requires React 19)
- Router state updates are wrapped in
- When set to
true- Router state updates remain wrapped in
React.startTransition(as they are without the flag) Link/Formnavigations will be wrapped inReact.startTransition- You can drop down to
useNavigate/useSubmitif you wish to opt out of this outerReact.startTransitioncall for the navigation
- You can drop down to
- A subset of router state info will be surfaced to the UI during navigations via
React.useOptimistic(i.e.,useNavigation(),useFetchers(), etc.)- โ ๏ธ This is a React 19 API so you must also be React 19 to opt into this flag for Framework/Data Mode
- Router state updates remain wrapped in
- When set to
false- The router will not leverage
React.startTransitionorReact.useOptimisticon any navigations or state changes
- The router will not leverage
- Declarative Mode
<BrowserRouter unstable_useTransitions>- When left unset
- Router state updates are wrapped in
React.startTransition
- Router state updates are wrapped in
- When set to
true- Router state updates remain wrapped in
React.startTransition(as they are without the flag) Link/Formnavigations will be wrapped inReact.startTransition
- Router state updates remain wrapped in
- When set to
false- The router will not leverage
React.startTransitionon any navigations or state changes
- The router will not leverage
Full Changelog: v7.9.6...v7.10.0
v7.9.6
Date: 2025-11-13
Security Notice
This release addresses 1 security vulnerability:
Patch Changes
react-router- Properly handle ancestor thrown middleware errors beforenext()on fetcher submissions (#14517)react-router- Fix issue with splat routes interfering with multiple calls topatchRoutesOnNavigation(#14487)react-router- Normalize double-slashes inresolvePath(#14529)@react-router/dev- Use a dynamicimport()to load ESM-onlyp-mapdependency to avoid issues on Node 20.18 and below (#14492)@react-router/dev- Short circuitHEADdocument requests before callingrenderToPipeableStreamin the defaultentry.server.tsxto more closely align with the spec (#14488)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Addlocation/paramsas arguments to client-sideunstable_onErrorto permit enhanced error reporting (#14509)-
โ ๏ธ This is a breaking change if you've already adopted
unstable_onError -
The second parameter has changed to an object including
errorInfo,location, andparams:// <RouterProvider unstable_onError={errorHandler} /> // <HydratedRouter unstable_onError={errorHandler} /> // Before function errorHandler(error: unknown, errorInfo?: React.errorInfo) { /*...*/ } // After function errorHandler( error: unknown, info: { location: Location; params: Params; errorInfo?: React.ErrorInfo; }, ) { /*...*/ }
-
Full Changelog: v7.9.5...v7.9.6
v7.9.5
Date: 2025-10-29
What's Changed
Instrumentation (unstable)
This release adds new unstable_instrumentation APIs that will allow you to add runtime instrumentation logic to various aspects of your application (server handler, client navigations/fetches, loaders, actions, middleware, route.lazy). For more information, please see the docs.
Patch Changes
react-router- Ensure action handlers run for routes with middleware even if no loader is present (#14443)@react-router/dev- Ensure route navigation doesn't remove CSSlinkelements used by dynamic imports (#14463)@react-router/dev- Typegen: only register route module types for routes within the app directory (#14439)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
-
react-router- Moveunstable_RSCHydratedRouterand utils toreact-router/domexport (#14457) -
react-router- Add a type-safehandlefield tounstable_useRoute()(#14462)For example:
// app/routes/admin.tsx const handle = { hello: "world" };// app/routes/some-other-route.tsx export default function Component() { const admin = useRoute("routes/admin"); if (!admin) throw new Error("Not nested within 'routes/admin'"); console.log(admin.handle); // ^? { hello: string } } -
react-router- Addunstable_instrumentationsAPI to allow users to add observability to their apps by instrumenting route loaders, actions, middlewares, lazy, as well as server-side request handlers and client side navigations/fetches (#14412)- Framework Mode:
entry.server.tsx:export const unstable_instrumentations = [...]entry.client.tsx:<HydratedRouter unstable_instrumentations={[...]} />
- Data Mode
createBrowserRouter(routes, { unstable_instrumentations: [...] })
- Framework Mode:
-
react-router- Add a newunstable_patternparameter to loaders/actions/middleware which contains the un-interpolated route pattern (i.e.,/blog/:slug) which is useful for aggregating logs/metrics by route in instrumentation code (#14412) -
@react-router/dev- Introduce aprerender.unstable_concurrencyoption, to support running the pre-rendering concurrently, potentially speeding up the build (#14380)
Full Changelog: v7.9.4...v7.9.5
v7.9.4
Date: 2025-10-08
Security Notice
This release addresses 1 security vulnerability:
What's Changed
useRoute() (unstable)
This release includes a new unstable_useRoute() hook that provides a type-safe way to access route loaderData/actionData from a specific route in Framework Mode. Think if it like a better version of useRouteLoaderData that works with the typegen system and also supports actionData. Check out the changelog entry below for more information.
Patch Changes
@react-router/dev- Updatevalibotdependency to^1.1.0(#14379)@react-router/node- Validate format of incoming session ids increateFileSessionStorage(#14426)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
-
react-router- handle external redirects in from server actions (#14400) -
react-router- New (unstable)useRoutehook for accessing data from specific routes (#14407)For example, let's say you have an
adminroute somewhere in your app and you want any child routes ofadminto all have access to theloaderDataandactionDatafromadmin.// app/routes/admin.tsx import { Outlet } from "react-router"; export const loader = () => ({ message: "Hello, loader!" }); export const action = () => ({ count: 1 }); export default function Component() { return ( <div> {/* ... */} <Outlet /> {/* ... */} </div> ); }You might even want to create a reusable widget that all of the routes nested under
admincould use:import { unstable_useRoute as useRoute } from "react-router"; export function AdminWidget() { // How to get `message` and `count` from `admin` route? }In framework mode,
useRouteknows all your app's routes and gives you TS errors when invalid route IDs are passed in:export function AdminWidget() { const admin = useRoute("routes/dmin"); // ^^^^^^^^^^^ }useRoutereturnsundefinedif the route is not part of the current page:export function AdminWidget() { const admin = useRoute("routes/admin"); if (!admin) { throw new Error(`AdminWidget used outside of "routes/admin"`); } }Note: the
rootroute is the exception since it is guaranteed to be part of the current page. As a result,useRoutenever returnsundefinedforroot.loaderDataandactionDataare marked as optional since they could be accessed before theactionis triggered or after theloaderthrew an error:export function AdminWidget() { const admin = useRoute("routes/admin"); if (!admin) { throw new Error(`AdminWidget used outside of "routes/admin"`); } const { loaderData, actionData } = admin; console.log(loaderData); // ^? { message: string } | undefined console.log(actionData); // ^? { count: number } | undefined }If instead of a specific route, you wanted access to the current route's
loaderDataandactionData, you can calluseRoutewithout arguments:export function AdminWidget() { const currentRoute = useRoute(); currentRoute.loaderData; currentRoute.actionData; }This usage is equivalent to calling
useLoaderDataanduseActionData, but consolidates all route data access into one hook:useRoute.Note: when calling
useRoute()(without a route ID), TS has no way to know which route is the current route. As a result,loaderDataandactionDataare typed asunknown. If you want more type-safety, you can either narrow the type yourself with something likezodor you can refactor your app to pass down typed props to yourAdminWidget:export function AdminWidget({ message, count, }: { message: string; count: number; }) { /* ... */ }
Full Changelog: v7.9.3...v7.9.4
v7.9.3
Date: 2025-09-26
Patch Changes
react-router- Fix Data Mode regression causing a 404 during initial load in whenmiddlewareexists without anyloaderfunctions (#14393)react-router- Do not try to useturbo-streamto decode CDN errors that never reached the server (#14385)- This was logic we used to have in Remix v2 that got lost in the adoption of Single Fetch
- This permits the actual CDN error to bubble to the
ErrorBoundaryinstead of a generic "Unable to decode turbo-stream response" error
Full Changelog: v7.9.2...v7.9.3
v7.9.2
Date: 2025-09-24
What's Changed
This release contains a handful of bug fixes, but we think you'll be most excited about the new unstable stuff ๐.
RSC Framework Mode (unstable)
This release includes our first release of unstable support for RSC in Framework Mode! You can read more about it in our blog post and the docs.
Fetcher Reset (unstable)
This release also includes a new (long-requested) fetcher.unstable_reset() API to reset fetchers back to their initial idle state.
Patch Changes
react-router- Ensure client-side router runs clientmiddlewareduring initialization data load (if required) even if no loaders exist (#14348)react-router- Fixmiddlewareprop not being supported on<Route>when used with a data router viacreateRoutesFromElements(#14357)react-router- UpdatecreateRoutesStubto work withmiddleware(#14348)- You will need to set the
<RoutesStub future={{ v8_middleware: true }} />flag to enable the propercontexttype
- You will need to set the
react-router- Update Lazy Route Discovery manifest requests to use a singular comma-separatedpathsquery param instead of repeatedpquery params (#14321)- This is because Cloudflare has a hard limit of 100 URL search param key/value pairs when used as a key for caching purposes
- If more that 100 paths were included, the cache key would be incomplete and could produce false-positive cache hits
react-router- Fail gracefully on manifest version mismatch logic ifsessionStorageaccess is blocked (#14335)react-router- UpdateuseOutletreturned element to have a stable identity in-between route changes (#13382)react-router- Handle encoded question mark and hash characters in ancestor splat routes (#14249)@react-router/dev- Switch internal vite plugin Response logic to use@remix-run/node-fetch-server(#13927)@react-router/dev- Fixpresetsfutureflags being ignored during config resolution (#14369)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Addfetcher.unstable_reset()API (#14206)react-router- In RSC Data Mode, handle SSR'd client errors and re-try in the browser (#14342)react-router- Enable full transition support for the RSC router (#14362)@react-router/dev- Add unstable support for RSC Framework Mode (#14336)@react-router/serve- Disablecompression()middleware in RSC framework mode (#14381)
Full Changelog: v7.9.1...v7.9.2
v7.9.1
Date: 2025-09-12
Patch Changes
- Fix internal
Futureinterface naming frommiddleware->v8_middleware(#14327)
Full Changelog: v7.9.0...v7.9.1
v7.9.0
Date: 2025-09-12
Security Notice
This release addresses 1 security vulnerability:
What's Changed
Stable Middleware and Context APIs
We have removed the unstable_ prefix from the following APIs and they are now considered stable and ready for production use:
RouterContextProvidercreateContextcreateBrowserRoutergetContextoption<HydratedRouter>getContextprop
Please see the Middleware Docs, the Middleware RFC, and the Client-side Context RFC for more information.
Minor Changes
- Stabilize middleware and context APIs (#14215)
Patch Changes
react-router- Updatehref()to correctly process routes that have an extension after the parameter or are a single optional parameter (#13797)react-router- Escape HTML inmeta()JSON-LD content (#14316)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- RSC: Add react-serverAwaitcomponent implementation (#14261)react-router- RSC: Fix hydration errors for routes that only have client loaders when using RSC in Data Mode along with a custom basename (#14264)react-router- RSC: Makehreffunction available in areact-servercontext (#14262)react-router- RSC: Decode each timegetPayload()is called to allow for "in-context" decoding and hoisting of contextual assets (#14248)
Full Changelog: v7.8.2...v7.9.0
v7.8.2
Date: 2025-08-22
Patch Changes
react-router- MaintainReadonlyMapandReadonlySettypes in server response data. (#13092)react-router- Fixbasenameusage without a leading slash in data routers (#11671)react-router- FixTypeErrorif you throw frompatchRoutesOnNavigationwhen no partial matches exist (#14198)react-router- Properly escape interpolated param values ingeneratePath()(#13530)@react-router/dev- Fix potential memory leak in defaultentry.server(#14200)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
Client-side onError
react-router- Add<RouterProvider unstable_onError>/<HydratedRouter unstable_onError>prop for client side error reporting (#14162)
Middleware
react-router- Delay serialization of.dataredirects to 202 responses until after middleware chain (#14205)react-router- Update client middleware so it returns thedataStrategyresults up the chain allowing for more advanced post-processing middleware (#14151, #14212)react-router- Remove Data Modefuture.unstable_middlewareflag fromcreateBrowserRouter(#14213)- This is only needed as a Framework Mode flag because of the route modules and the
getLoadContexttype behavior change - In Data Mode, it's an opt-in feature because it's just a new property on a route object, so there's no behavior changes that necessitate a flag
- This is only needed as a Framework Mode flag because of the route modules and the
RSC
react-router- Allow opting out of revalidation on server actions with hidden$SKIP_REVALIDATIONinput (#14154)
Full Changelog: v7.8.1...v7.8.2
v7.8.1
Date: 2025-08-15
Patch Changes
react-router- Fix usage of optional path segments in nested routes defined using absolute paths (#14135)react-router- Fix optional static segment matching inmatchPath(#11813)react-router- Fix pre-rendering when abasenameis set withssr:false(#13791)react-router- Properly convert returned/throwndata()values toResponseinstances viaResponse.json()in resource routes and middleware (#14159, #14181)@react-router/dev- Update generatedRoute.MetaArgstype soloaderDatais only potentially undefined when anErrorBoundaryexport is present (#14173)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
Middleware
react-router- Bubble client pre-nextmiddleware errors to the shallowest ancestor that needs to load, not strictly the shallowest ancestor with a loader (#14150)react-router- Propagate non-redirectResponsevalues thrown from middleware to the error boundary on document/data requests (#14182)
RSC
react-router- ProvideisRouteErrorResponseutility inreact-serverenvironments (#14166)react-router- HandlemetaandlinksRoute Exports in RSC Data Mode (#14136)
Full Changelog: v7.8.0...v7.8.1
v7.8.0
Date: 2025-08-07
What's Changed
Consistently named loaderData values
Ever noticed the discrepancies in loader data values handed to you by the framework? Like, we call it loaderData in your component props, but then match.data in your matches? Yeah, us too - as well as some keen-eyed React Router users who raised this in a proposal. We've added new loaderData fields alongside existing data fields in a few lingering spots to align with the loaderData naming used in the new Route.* APIs.
Improvements/fixes to the middleware APIs (unstable)
The biggest set of changes in 7.8.0 are to the unstable_middleware API's as we move closer to stabilizing them. If you've adopted the middleware APIs for early testing, please read the middleware changes below carefully. We hope to stabilize these soon so please let us know of any feedback you have on the API's in their current state!
Minor Changes
react-router- Addnonceprop toLinks&PrefetchPageLinks(#14048)react-router- AddloaderDataarguments/properties alongside existingdataarguments/properties to provide consistency and clarity betweenloaderDataandactionDataacross the board (#14047)- Updated types:
Route.MetaArgs,Route.MetaMatch,MetaArgs,MetaMatch,Route.ComponentProps.matches,UIMatch @deprecatedwarnings have been added to the existingdataproperties to point users to newloaderDataproperties, in preparation for removing thedataproperties in a future major release
- Updated types:
Patch Changes
-
react-router- Prevent "Did not find corresponding fetcher result" console error when navigating during afetcher.submitrevalidation (#14114) -
react-router- Switch Lazy Route Discovery manifest URL generation to use a standaloneURLSearchParamsinstance instead ofURL.searchParamsto avoid a major performance bottleneck in Chrome (#14084) -
react-router- Adjust internal RSC usage ofReact.useto avoid Webpack compilation errors when using React 18 (#14113) -
react-router- Remove dependency on@types/nodein TypeScript declaration files (#14059) -
react-router- Fix types forUIMatchto reflect that theloaderData/dataproperties may beundefined(#12206)-
When an
ErrorBoundaryis being rendered, not all active matches will have loader data available, since it may have been theirloaderthat threw to trigger the boundary -
The
UIMatch.datatype was not correctly handing this and would always reflect the presence of data, leading to the unexpected runtime errors when anErrorBoundarywas rendered -
โ ๏ธ This may cause some type errors to show up in your code for unguarded
match.dataaccesses - you should properly guard forundefinedvalues in those scenarios.// app/root.tsx export function loader() { someFunctionThatThrows(); // โ Throws an Error return { title: "My Title" }; } export function Layout({ children }: { children: React.ReactNode }) { let matches = useMatches(); let rootMatch = matches[0] as UIMatch<Awaited<ReturnType<typeof loader>>>; // ^ rootMatch.data is currently incorrectly typed here, so TypeScript does // not complain if you do the following which throws an error at runtime: let { title } = rootMatch.data; // ๐ฅ return <html>...</html>; }
-
-
@react-router/dev- Fix rename without mkdir in Vite plugin (#14105)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
RSC
react-router- Fix Data Mode issue where routes that returnfalsefromshouldRevalidatewould be replaced by an<Outlet />(#14071)react-router- Proxy server action side-effect redirects from actions for document andcallServerrequests (#14131)
Middleware
-
react-router- Change theunstable_getContextsignature onRouterProvider,HydratedRouter, andunstable_RSCHydratedRouterso that it returns anunstable_RouterContextProviderinstance instead of aMapused to construct the instance internally (#14097)- See the docs for more information
- โ ๏ธ This is a breaking change if you have adopted the
unstable_getContextprop
-
react-router- Run client middleware on client navigations even if no loaders exist (#14106) -
react-router- Convert internal middleware implementations to use the newunstable_generateMiddlewareResponseAPI (#14103) -
react-router- Ensure resource route errors go throughhandleErrorw/middleware enabled (#14078) -
react-router- Propagate returnedResponsefrom server middleware ifnextwasn't called (#14093) -
react-router- Allow server middlewares to returndata()values which will be converted into aResponse(#14093, #14128) -
react-router- Update middleware error handling so that thenextfunction never throws and instead handles any middleware errors at the properErrorBoundaryand returns theResponseup through the ancestornextfunction (#14118)- See the error handling docs for more information
- โ ๏ธ This changes existing functionality so if you are currently wrapping
nextcalls intry/catchyou should be able to remove those
-
react-router- Bubble client-side middleware errors prior tonextto the appropriate ancestor error boundary (#14138) -
react-router- When middleware is enabled, make thecontextparameter read-only (Readonly<unstable_RouterContextProvider>) so that TypeScript will not allow you to write arbitrary fields to it in loaders, actions, or middleware. (#14097) -
react-router- Rename and alter the signature/functionality of theunstable_respondAPI instaticHandler.query/staticHandler.queryRoute(#14103)-
This only impacts users using
createStaticHandler()for manual data loading during non-Framework Mode SSR -
The API has been renamed to
unstable_generateMiddlewareResponsefor clarity -
The main functional change is that instead of running the loaders/actions before calling
unstable_respondand handing you the result, we now pass aquery/queryRoutefunction as a parameter and you execute the loaders/actions inside your callback, giving you full access to pre-processing and error handling -
The
queryversion of the API now has a signature of(query: (r: Request) => Promise<StaticHandlerContext | Response>) => Promise<Response> -
The
queryRouteversion of the API now has a signature of(queryRoute: (r: Request) => Promise<Response>) => Promise<Response> -
This allows for more advanced usages such as running logic before/after calling
queryand direct error handling of errors thrown from query -
โ ๏ธ This is a breaking change if you've adopted the
staticHandlerunstable_respondAPIlet response = await staticHandler.query(request, { requestContext: new unstable_RouterContextProvider(), async unstable_generateMiddlewareResponse(query) { try { // At this point we've run middleware top-down so we need to call the // handlers and generate the Response to bubble back up the middleware let result = await query(request); if (isResponse(result)) { return result; // Redirects, etc. } return await generateHtmlResponse(result); } catch (error: unknown) { return generateErrorResponse(error); } }, });
-
-
@react-router/{architect,cloudflare,express,node}- Change thegetLoadContextsignature (type GetLoadContextFunction) whenfuture.unstable_middlewareis enabled so that it returns anunstable_RouterContextProviderinstance instead of aMapused to construct the instance internally (#14097)- This also removes the
type unstable_InitialContextexport - See the middleware
getLoadContextdocs for more information - โ ๏ธ This is a breaking change if you have adopted middleware and are using a custom server with a
getLoadContextfunction
- This also removes the
Changes by Package
create-react-routerreact-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/serve
Full Changelog: v7.7.1...v7.8.0
v7.7.1
Date: 2025-07-24
Patch Changes
@react-router/dev- Update to Prettier v3 for formatting when runningreact-router reveal --no-typescript(#14049)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- RSC Data Mode: fix bug where routes with errors weren't forced to revalidate whenshouldRevalidatereturnedfalse(#14026)react-router- RSC Data Mode: fixMatched leaf route at location "/..." does not have an element or Componentwarnings when error boundaries are rendered (#14021)
Full Changelog: v7.7.0...v7.7.1
v7.7.0
Date: 2025-07-16
What's Changed
Unstable RSC APIs
We're excited to introduce experimental support for RSC in Data Mode via the following new APIs:
unstable_RSCHydratedRouterunstable_RSCStaticRouterunstable_createCallServerunstable_getRSCStreamunstable_matchRSCServerRequestunstable_routeRSCServerRequest
For more information, check out the blog post and the RSC Docs.
Minor Changes
create-react-router- Add Deno as a supported and detectable package manager. Note that this detection will only work with Deno versions 2.0.5 and above. If you are using an older version version of Deno then you must specify the --package-manager CLI flag set todeno. (#12327)@react-router/remix-config-routes-adapter- ExportDefineRouteFunctiontype alongsideDefineRoutesFunction(#13945)
Patch Changes
-
react-router- HandleInvalidCharacterErrorwhen validating cookie signature (#13847) -
react-router- Pass a copy ofsearchParamsto thesetSearchParamscallback function to avoid mutations of the internalsearchParamsinstance (#12784)- This causes bugs if you mutate the current stateful
searchParamswhen a navigation is blocked because the internal instance gets out of sync withuseLocation().search
- This causes bugs if you mutate the current stateful
-
react-router- Support invalidDateinturbo-streamv2 fork (#13684) -
react-router- In Framework Mode, clear critical CSS in development after initial render (#13872, #13995) -
react-router- Strip search parameters frompatchRoutesOnNavigationpathparam for fetcher calls (#13911) -
react-router- Skip scroll restoration onuseRevalidator()calls because they're not new locations (#13671) -
react-router- Support unencoded UTF-8 routes in prerender config withssrset tofalse(#13699) -
react-router- Do not throw if the url hash is not a valid URI component (#13247) -
react-router- RemoveContent-Lengthheader from Single Fetch responses (#13902) -
react-router- Fix a regression increateRoutesStubintroduced with the middleware feature (#13946)-
As part of that work we altered the signature to align with the new middleware APIs without making it backwards compatible with the prior
AppLoadContextAPI -
This permitted
createRoutesStubto work if you were opting into middleware and the updatedcontexttypings, but brokecreateRoutesStubfor users not yet opting into middleware -
We've reverted this change and re-implemented it in such a way that both sets of users can leverage it
-
โ ๏ธ This may be a breaking bug for if you have adopted the unstable Middleware feature and are using
createRoutesStubwith the updated API.// If you have not opted into middleware, the old API should work again let context: AppLoadContext = { /*...*/ }; let Stub = createRoutesStub(routes, context); // If you have opted into middleware, you should now pass an instantiated // `unstable_routerContextProvider` instead of a `getContext` factory function. let context = new unstable_RouterContextProvider(); context.set(SomeContext, someValue); let Stub = createRoutesStub(routes, context);
-
-
@react-router/dev- Updatevite-nodeto^3.2.2to support Vite 7 (#13781) -
@react-router/dev- Properly handlehttpsprotocol in dev mode (#13746) -
@react-router/dev- Fix missing styles when Vite'sbuild.cssCodeSplitoption is disabled (#13943) -
@react-router/dev- Allow.mtsand.mjsextensions for route config file (#13931) -
@react-router/dev- Fix prerender file locations whencwddiffers from project root (#13824) -
@react-router/dev- Improve chunk error logging when a chunk cannot be found during the build (#13799) -
@react-router/dev- Fix incorrectly configuredexternalConditionswhich had enabledmodulecondition for externals and broke builds with certain packages (like Emotion) (#13871)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
- Add unstable RSC support for Data Mode (#13700)
- For more information, see the RSC documentation
Changes by Package
create-react-routerreact-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/serve
Full Changelog: v7.6.3...v7.7.0
v7.6.3
Date: 2025-06-27
Patch Changes
-
react-router- Do not serialize types foruseRouteLoaderData<typeof clientLoader>(#13752)-
For types to distinguish a
clientLoaderfrom aserverLoader, you MUST annotateclientLoaderargs:// ๐ annotation required to skip serializing types export function clientLoader({}: Route.ClientLoaderArgs) { return { fn: () => "earth" }; } function SomeComponent() { const data = useRouteLoaderData<typeof clientLoader>("routes/this-route"); const planet = data?.fn() ?? "world"; return <h1>Hello, {planet}!</h1>; }
-
-
@react-router/cloudflare- RemovetsupfrompeerDependencies(#13757) -
@react-router/dev- Add Vite 7 support (#13748) -
@react-router/dev- Skippackage.jsonresolution checks when a customentry.server.(j|t)sxfile is provided (#13744) -
@react-router/dev- Add validation for a route's id not being 'root' (#13792) -
@react-router/fs-routes@react-router/remix-config-routes-adapter- UsereplaceAllfor normalizing windows file system slashes (#13738) -
@react-router/node- Remove old "install" package exports (#13762)
Full Changelog: v7.6.2...v7.6.3
v7.6.2
Date: 2025-06-03
Patch Changes
-
create-react-router- Updatetar-fs(#13675) -
react-router- (INTERNAL) Slight refactor of internalheaders()function processing for use with RSC (#13639) -
react-router@react-router/dev- Avoid additionalwith-propschunk in Framework Mode by moving route module component prop logic from the Vite plugin toreact-router(#13650) -
@react-router/dev- Whenfuture.unstable_viteEnvironmentApiis enabled and an absolute Vitebasehas been configured, ensure critical CSS is handled correctly during development (#13598) -
@react-router/dev- Updatevite-node(#13673) -
@react-router/dev- Fix typegen for non-{.js,.jsx,.ts,.tsx} routes like .mdx (#12453) -
@react-router/dev- Fix href types for optional dynamic params (#13725)7.6.1 introduced fixes for
hrefwhen using optional static segments, but those fixes caused regressions with how optional dynamic params worked in 7.6.0:// 7.6.0 href("/users/:id?"); // โ href("/users/:id?", { id: 1 }); // โ // 7.6.1 href("/users/:id?"); // โ href("/users/:id?", { id: 1 }); // โNow, optional static segments are expanded into different paths for
href, but optional dynamic params are not. This wayhrefcan unambiguously refer to an exact URL path, all while keeping the number of path options to a minimum.// 7.6.2 // path: /users/:id?/edit? href(" // ^ suggestions when cursor is here: // // /users/:id? // /users/:id?/editAdditionally, you can pass
paramsfrom component props without needing to narrow them manually:declare const params: { id?: number }; // 7.6.0 href("/users/:id?", params); // 7.6.1 href("/users/:id?", params); // โ "id" in params ? href("/users/:id", params) : href("/users"); // works... but is annoying // 7.6.2 href("/users/:id?", params); // restores behavior of 7.6.0
Full Changelog: v7.6.1...v7.6.2
v7.6.1
Date: 2025-05-25
Patch Changes
-
react-router- Partially revert optimization added in7.1.4to reduce calls tomatchRoutesbecause it surfaced other issues (#13562) -
react-router- UpdateRoute.MetaArgsto reflect thatdatacan be potentiallyundefined(#13563)- This is primarily for cases where a route
loaderthrew an error to it's ownErrorBoundary, but it also arises in the case of a 404 which renders the rootErrorBoundary/metabut the rootloaderdid not run because not routes matched
- This is primarily for cases where a route
-
react-router- Avoid initial fetcher execution 404 error when Lazy Route Discovery is interrupted by a navigation (#13564) -
react-router- Properlyhrefreplaces splats*(#13593)href("/products/*", { "*": "/1/edit" }); // -> /products/1/edit
-
@react-router/architect- Update@architect/functionsfrom^5.2.0to^7.0.0(#13556) -
@react-router/dev- Prevent typegen with route files that are outside theapp/directory (#12996) -
@react-router/dev- Add additional logging tobuildcommand output when cleaning assets from server build (#13547) -
@react-router/dev- Don't clean assets from server build whenbuild.ssrEmitAssetshas been enabled in Vite config (#13547) -
@react-router/dev- Fix typegen when same route is used at multiple paths (#13574)-
For example,
routes/route.tsxis used at 4 different paths here:import { type RouteConfig, route } from "@react-router/dev/routes"; export default [ route("base/:base", "routes/base.tsx", [ route("home/:home", "routes/route.tsx", { id: "home" }), route("changelog/:changelog", "routes/route.tsx", { id: "changelog" }), route("splat/*", "routes/route.tsx", { id: "splat" }), ]), route("other/:other", "routes/route.tsx", { id: "other" }), ] satisfies RouteConfig; -
Previously, typegen would arbitrarily pick one of these paths to be the "winner" and generate types for the route module based on that path
-
Now, typegen creates unions as necessary for alternate paths for the same route file
-
-
@react-router/dev- Better types forparams(#13543)-
For example:
// routes.ts import { type RouteConfig, route } from "@react-router/dev/routes"; export default [ route("parent/:p", "routes/parent.tsx", [ route("route/:r", "routes/route.tsx", [ route("child1/:c1a/:c1b", "routes/child1.tsx"), route("child2/:c2a/:c2b", "routes/child2.tsx"), ]), ]), ] satisfies RouteConfig; -
Previously,
paramsforroutes/routewere calculated as{ p: string, r: string }. -
This incorrectly ignores params that could come from child routes
-
If visiting
/parent/1/route/2/child1/3/4, the actual params passed toroutes/routewill have a type of{ p: string, r: string, c1a: string, c1b: string } -
Now,
paramsare aware of child routes and autocompletion will include child params as optionals:params.| // ^ cursor is here and you ask for autocompletion // p: string // r: string // c1a?: string // c1b?: string // c2a?: string // c2b?: string -
You can also narrow the types for
paramsas it is implemented as a normalized union of params for each page that includesroutes/route:if (typeof params.c1a === 'string') { params.| // ^ cursor is here and you ask for autocompletion // p: string // r: string // c1a: string // c1b: string }
-
-
@react-router/dev- Fixhreffor optional segments (#13595)-
Type generation now expands paths with optionals into their corresponding non-optional paths
-
For example, the path
/user/:id?gets expanded into/userand/user/:idto more closely model visitable URLs -
hrefthen uses these expanded (non-optional) paths to construct type-safe paths for your app:// original: /user/:id? // expanded: /user & /user/:id href("/user"); // โ href("/user/:id", { id: 1 }); // โ -
This becomes even more important for static optional paths where there wasn't a good way to indicate whether the optional should be included in the resulting path:
// original: /products/:id/detail? // before href("/products/:id/detail?"); // โ How can we tell `href` to include or omit `detail?` segment with a complex API? // now // expanded: /products/:id & /products/:id/detail href("/product/:id"); // โ href("/product/:id/detail"); // โ
-
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
@react-router/dev- Renamed internalreact-router/route-moduleexport toreact-router/internal(#13543)@react-router/dev- RemovedInfoexport from generated+types/*files (#13543)@react-router/dev- Normalize dirent entry path across node versions when generating SRI manifest (#13591)
Full Changelog: v7.6.0...v7.6.1
v7.6.0
Date: 2025-05-08
What's Changed
routeDiscovery Config Option
We've added a new config option in 7.6.0 which grants you more control over the Lazy Route Discovery feature. You can now configure the /__manifest path if you're running multiple RR applications on the same server, or you can also disable the feature entirely if your application is small enough and the feature isn't necessary.
// react-router.config.ts
export default {
// You can modify the manifest path used:
routeDiscovery: { mode: "lazy", manifestPath: "/custom-manifest" }
// Or you can disable this feature entirely and include all routes in the
// manifest on initial document load:
routeDiscovery: { mode: "initial" }
// If you don't specify anything, the default config is as follows, which enables
// Lazy Route Discovery and makes manifest requests to the `/__manifest` path:
// routeDiscovery: { mode: "lazy", manifestPath: "/__manifest" }
} satisfies Config;
Automatic Types for Future Flags
Some future flags alter the way types should work in React Router. Previously, you had to remember to manually opt-in to the new types. For example, for future.unstable_middleware:
// react-router.config.ts
// Step 1: Enable middleware
export default {
future: {
unstable_middleware: true,
},
};
// Step 2: Enable middleware types
declare module "react-router" {
interface Future {
unstable_middleware: true; // ๐ Enable middleware types
}
}
It was up to you to keep the runtime future flags synced with the types for those flags. This was confusing and error-prone.
Now, React Router will automatically enable types for future flags. That means you only need to specify the runtime future flag:
// react-router.config.ts
// Step 1: Enable middleware
export default {
future: {
unstable_middleware: true,
},
};
// No step 2! That's it!
Behind the scenes, React Router will generate the corresponding declare module into .react-router/types. Currently this is done in .react-router/types/+register.ts but this is an implementation detail that may change in the future.
Minor Changes
-
react-router- Added a newrouteDiscoveryoption inreact-router.config.tsto configure Lazy Route Discovery behavior (#13451) -
react-router- Add support for route component props increateRoutesStub(#13528)-
This allows you to unit test your route components using the props instead of the hooks:
let RoutesStub = createRoutesStub([ { path: "/", Component({ loaderData }) { let data = loaderData as { message: string }; return <pre data-testid="data">Message: {data.message}</pre>; }, loader() { return { message: "hello" }; }, }, ]); render(<RoutesStub />); await waitFor(() => screen.findByText("Message: hello"));
-
-
@react-router/dev- Automatic types for future flags (#13506)
Patch Changes
You may notice this list is a bit larger than usual! The team ate their vegetables last week and spent the week squashing bugs to work on lowering the issue count that had ballooned a bit since the v7 release.
react-router- Fixreact-routermodule augmentation forNodeNext(#13498)react-router- Don't bundlereact-routerinreact-router/domCJS export (#13497)react-router- Fix bug where a submittingfetcherwould get stuck in aloadingstate if a revalidatingloaderredirected (#12873)react-router- Fix hydration error if a serverloaderreturnedundefined(#13496)react-router- Fix initial load 404 scenarios in data mode (#13500)react-router- StabilizeuseRevalidator'srevalidatefunction (#13542)react-router- Preserve status code if aclientActionthrows adata()result in framework mode (#13522)react-router- Be defensive against leading double slashes in paths to avoidInvalid URLerrors from the URL constructor (#13510)- Note we do not sanitize/normalize these paths - we only detect them so we can avoid the error that would be thrown by
new URL("//", window.location.origin)
- Note we do not sanitize/normalize these paths - we only detect them so we can avoid the error that would be thrown by
react-router- RemoveNavigatordeclaration fornavigator.connection.saveDatato avoid messing with any other types beyondsaveDatain user land (#13512)react-router- FixhandleErrorparamsvalues on.datarequests for routes with a dynamic param as the last URL segment (#13481)react-router- Don't trigger anErrorBoundaryUI before the reload when we detect a manifest version mismatch in Lazy Route Discovery (#13480)react-router- Inlineturbo-stream@2.4.1dependency and fix decoding ordering ofMap/Setinstances (#13518)react-router- Only render dev warnings during dev (#13461)react-router- Short circuit post-processing on aborteddataStrategyrequests (#13521)- This resolves non-user-facing console errors of the form
Cannot read properties of undefined (reading 'result')
- This resolves non-user-facing console errors of the form
@react-router/dev- Support project root directories without apackage.jsonif it exists in a parent directory (#13472)@react-router/dev- When providing a custom Vite config path via the CLI--config/-cflag, default the project root directory to the directory containing the Vite config when not explicitly provided (#13472)@react-router/dev- In aroutes.tscontext, ensure the--modeflag is respected forimport.meta.env.MODE(#13485)- Previously,
import.meta.env.MODEwithin aroutes.tscontext was always"development"for thedevandtypegen --watchcommands, but otherwise resolved to"production". These defaults are still in place, but if a--modeflag is provided, this will now take precedence.
- Previously,
@react-router/dev- Ensure consistent project root directory resolution logic in CLI commands (#13472)@react-router/dev- When executingreact-router.config.tsandroutes.tswithvite-node, ensure that PostCSS config files are ignored (#13489)@react-router/dev- When extracting critical CSS during development, ensure it's loaded from the client environment to avoid issues with plugins that handle the SSR environment differently (#13503)@react-router/dev- Fix "Status message is not supported by HTTP/2" error during dev when using HTTPS (#13460)@react-router/dev- Update config whenreact-router.config.tsis created or deleted during development (#12319)@react-router/dev- Skip unnecessaryroutes.tsevaluation before Vite build is started (#13513)@react-router/dev- FixTS2300: Duplicate identifiererrors caused by generated types (#13499)- Previously, routes that had the same full path would cause duplicate entries in the generated types for
href(.react-router/types/+register.ts), causing type checking errors
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Fix a few bugs with error bubbling in middleware use-cases (#13538)@react-router/dev- Whenfuture.unstable_viteEnvironmentApiis enabled, ensure thatbuild.assetsDirin Vite config is respected whenenvironments.client.build.assetsDiris not configured (#13491)
Changes by Package
create-react-routerreact-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/serve
Full Changelog: v7.5.3...v7.6.0
v7.5.3
Date: 2025-04-28
Patch Changes
react-router- Fix bug where bubbled action errors would result inloaderDatabeing cleared at the handlingErrorBoundaryroute (#13476)react-router- Handle redirects fromclientLoader.hydrateinitial load executions (#13477)
Full Changelog: v7.5.2...v7.5.3
v7.5.2
Date: 2025-04-24
Security Notice
Fixed 2 security vulnerabilities that could result in cache-poisoning attacks by sending specific headers intended for build-time usage for SPA Mode and Pre-rendering (GHSA-f46r-rw29-r322, GHSA-cpj6-fhp6-mr6j).
Patch Changes
react-router- Adjust approach for Pre-rendering/SPA Mode via headers (#13453)react-router- Update Single Fetch to also handle the 204 redirects used in?_datarequests in Remix v2 (#13364)- This allows applications to trigger a redirect on
.datarequests from outside the scope of React Router (i.e., anexpress/honomiddleware) the same way they did in Remix v2 before Single Fetch was implemented - This is a bit of an escape hatch - the recommended way to handle this is redirecting from a root route middleware
- To use this functionality, you may return from a
.datarequest wih a response as follows:- Set a 204 status code
- Set an
X-Remix-Redirect: <new-location>header - Optionally, set
X-Remix-Replace: trueorX-Remix-Reload-Document: trueheaders to replicatereplace()/redirectDocument()functionality
- โ ๏ธ Please note that these responses rely on implementation details that are subject to change without a SemVer major release, and it is recommended you set up integration tests for your application to confirm this functionality is working correctly with each future React Router upgrade
- This allows applications to trigger a redirect on
Full Changelog: v7.5.1...v7.5.2
v7.5.1
Date: 2025-04-17
Patch Changes
-
react-router- When using the object-basedroute.lazyAPI, theHydrateFallbackandhydrateFallbackElementproperties are now skipped when lazy loading routes after hydration (#13376)-
If you move the code for these properties into a separate file, since the hydrate properties were unused already (if the route wasn't present during hydration), you can avoid downloading them at all. For example:
createBrowserRouter([ { path: "/show/:showId", lazy: { loader: async () => (await import("./show.loader.js")).loader, Component: async () => (await import("./show.component.js")).Component, HydrateFallback: async () => (await import("./show.hydrate-fallback.js")).HydrateFallback, }, }, ]);
-
-
react-router- Fix single fetch bug where no revalidation request would be made when navigating upwards to a reused parent route (#13253) -
react-router- Properly revalidate pre-rendered paths when param values change when usingssr:false+prerenderconfigs (#13380) -
react-router- Fix pre-rendering when a loader returns a redirect (#13365) -
react-router- Do not automatically addnulltostaticHandler.query()context.loaderDataif routes do not have loaders (#13223)- This was a Remix v2 implementation detail inadvertently left in for React Router v7
- Now that we allow returning
undefinedfrom loaders, our prior check ofloaderData[routeId] !== undefinedwas no longer sufficient and was changed to arouteId in loaderDatacheck - thesenullvalues can cause issues for this new check - โ ๏ธ This could be a "breaking bug fix" for you if you are doing manual SSR with
createStaticHandler()/<StaticRouterProvider>, and usingcontext.loaderDatato control<RouterProvider>hydration behavior on the client
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Add better error messaging whengetLoadContextis not updated to return aMap(#13242)react-router- Update context type forLoaderFunctionArgs/ActionFunctionArgswhen middleware is enabled (#13381)react-router- Add a newunstable_runClientMiddlewareargument todataStrategyto enable middleware execution in customdataStrategyimplementations (#13395)react-router- Add support for the newunstable_shouldCallHandler/unstable_shouldRevalidateArgsAPIs indataStrategy(#13253)
Full Changelog: v7.5.0...v7.5.1
v7.5.0
Date: 2025-04-04
What's Changed
route.lazy Object API
We've introduced a new route.lazy API which gives you more granular control over the lazy loading of route properties that you could not achieve with the route.lazy() function signature. This is useful for Framework mode and performance-critical library mode applications.
createBrowserRouter([
{
path: "/show/:showId",
lazy: {
loader: async () => (await import("./show.loader.js")).loader,
action: async () => (await import("./show.action.js")).action,
Component: async () => (await import("./show.component.js")).Component,
},
},
]);
โ ๏ธ This is a breaking change if you have adopted the route.unstable_lazyMiddleware API which has been removed in favor of route.lazy.unstable_middleware. See the Unstable Changes section below for more information.
Minor Changes
react-router- Add granular object-based API forroute.lazyto support lazy loading of individual route properties (#13294)
Patch Changes
@react-router/dev- Update optionalwranglerpeer dependency range to supportwranglerv4 (#13258)@react-router/dev- Reinstate dependency optimization in the child compiler to fixdepsOptimizer is required in dev modeerrors when usingvite-plugin-cloudflareand importing Node.js builtins (#13317)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Introducefuture.unstable_subResourceIntegrityflag that enables generation of animportmapwithintegrityfor the scripts that will be loaded by the browser (#13163)react-router- Remove support for theroute.unstable_lazyMiddlewareproperty (#13294)- In order to lazily load middleware, you can use the new object-based
route.lazy.unstable_middlewareAPI
- In order to lazily load middleware, you can use the new object-based
@react-router/dev- Whenfuture.unstable_viteEnvironmentApiis enabled, ensure critical CSS in development works when using a custom Vitebasehas been configured (#13305)
Changes by Package
create-react-routerreact-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/serve
Full Changelog: v7.4.1...v7.5.0
v7.4.1
Date: 2025-03-28
Security Notice
Fixed a security vulnerability that allowed URL manipulation and potential cache pollution via the Host and X-Forwarded-Host headers due to inadequate port sanitization (GHSA-4q56-crqp-v477/CVE-2025-31137).
Patch Changes
react-router- Dedupe calls toroute.lazyfunctions (#13260)@react-router/dev- Fix path in prerender error messages (#13257)@react-router/dev- Fix typegen for virtual modules whenmoduleDetectionis set toforce(#13267)@react-router/express- Better validation ofx-forwarded-hostheader to prevent potential security issues (#13309)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Fix types onunstable_MiddlewareFunctionto avoid type errors when a middleware doesn't return a value (#13311)react-router- Add support forroute.unstable_lazyMiddlewarefunction to allow lazy loading of middleware logic (#13210)- โ ๏ธ We do not recommend adoption of this API currently as we are likely going to change it prior to the stable release of middleware
- โ ๏ธ This may be a breaking change if your app is currently returning
unstable_middlewarefromroute.lazy - The
route.unstable_middlewareproperty is no longer supported in the return value fromroute.lazy - If you want to lazily load middleware, you must use
route.unstable_lazyMiddleware
@react-router/dev- When bothfuture.unstable_middlewareandfuture.unstable_splitRouteModulesare enabled, splitunstable_clientMiddlewareroute exports into separate chunks when possible (#13210)@react-router/dev- Improve performance offuture.unstable_middlewareby ensuring that route modules are only blocking during the middleware phase when theunstable_clientMiddlewarehas been defined (#13210)
Full Changelog: v7.4.0...v7.4.1
v7.4.0
Date: 2025-03-19
Minor Changes
@react-router/dev- Generate types forvirtual:react-router/server-buildmodule (#13152)
Patch Changes
react-router- Fix root loader data on initial load redirects in SPA mode (#13222)react-router- Load ancestor pathless/index routes in lazy route discovery for upwards non-eager-discovery routing (#13203)react-router- FixshouldRevalidatebehavior forclientLoader-only routes inssr:trueapps (#13221)@react-router/dev- Fix conflicts with other Vite plugins that use theconfigureServerand/orconfigurePreviewServerhooks (#13184)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- If a middleware throws an error, ensure we only bubble the error itself vianext()and are no longer leaking theMiddlewareErrorimplementation detail (#13180)- โ ๏ธ This may be a breaking change if you are
catch-ing errors thrown by thenext()function in your middlewares
- โ ๏ธ This may be a breaking change if you are
react-router- FixRequestHandlerloadContextparameter type when middleware is enabled (#13204)react-router- UpdateRoute.unstable_MiddlewareFunctionto have a return value ofResponse | undefinedinstead ofResponse | void(#13199)@react-router/dev- Whenfuture.unstable_splitRouteModulesis set to"enforce", allow both splittable and unsplittable root route exports since it's always in a single chunk (#13238)@react-router/dev- Whenfuture.unstable_viteEnvironmentApiis enabled, allow plugins that override the default SSR environment (such as@cloudflare/vite-plugin) to be placed before or after the React Router plugin (#13183)
Changes by Package
create-react-routerreact-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/serve
Full Changelog: v7.3.0...v7.4.0
v7.3.0
Date: 2025-03-06
Minor Changes
- Add
fetcherKeyas a parameter topatchRoutesOnNavigation(#13061)
Patch Changes
react-router- Detect and handle manifest-skew issues on new deploys during active sessions (#13061)- In framework mode, Lazy Route Discovery will now detect manifest version mismatches in active sessions after a new deploy
- On navigations to undiscovered routes, this mismatch will trigger a document reload of the destination path
- On
fetchercalls to undiscovered routes, this mismatch will trigger a document reload of the current path
react-router- Skip resource route flow in dev server in SPA mode (#13113)react-router- Fix single fetch_root.datarequests when abasenameis used (#12898)react-router- Fix types forloaderDataandactionDatathat containedRecords (#13139)- โ ๏ธ This is a breaking change for users who have already adopted
unstable_SerializesTo- see the note in theUnstable Changessection below for more information
- โ ๏ธ This is a breaking change for users who have already adopted
@react-router/dev- Fix support for custom clientbuild.rollupOptions.output.entryFileNames(#13098)@react-router/dev- Fix usage ofprerenderoption whenserverBundlesoption has been configured or provided by a preset, e.g.vercelPresetfrom@vercel/react-router(#13082)@react-router/dev- Fix support for custombuild.assetsDir(#13077)@react-router/dev- Remove unused dependencies (#13134)@react-router/dev- Stub all routes except root in "SPA Mode" server builds to avoid issues when route modules or their dependencies import non-SSR-friendly modules (#13023)@react-router/dev- Remove unused Vite file system watcher (#13133)@react-router/dev- Fix support for custom SSR build input whenserverBundlesoption has been configured (#13107)- โ ๏ธ Note that for consumers using the
future.unstable_viteEnvironmentApiandserverBundlesoptions together, hyphens are no longer supported in server bundle IDs since they also need to be valid Vite environment names.
- โ ๏ธ Note that for consumers using the
@react-router/dev- Fix dev server when using HTTPS by stripping HTTP/2 pseudo headers from dev server requests (#12830)@react-router/dev- Lazy load Cloudflare platform proxy on first dev server request when using thecloudflareDevProxyVite plugin to avoid creating unnecessaryworkerdprocesses (#13016)@react-router/dev- Fix duplicated entries in typegen for layout routes and their corresponding index route (#13140)@react-router/express- UpdateexpresspeerDependencyto include v5 (https://github.com/remix-run/react-router/pull/13064) (#12961)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Addcontextsupport to client side data routers (unstable) (#12941)react-router- Support middleware on routes (unstable) (#12941)@react-router/dev- Fix errors withfuture.unstable_viteEnvironmentApiwhen thessrenvironment has been configured by another plugin to be a customVite.DevEnvironmentrather than the defaultVite.RunnableDevEnvironment(#13008)@react-router/dev- Whenfuture.unstable_viteEnvironmentApiis enabled and thessrenvironment hasoptimizeDeps.noDiscoverydisabled, defineoptimizeDeps.entriesandoptimizeDeps.include(#13007)
Client-side context (unstable)
Your application clientLoader/clientAction functions (or loader/action in library mode) will now receive a context parameter on the client. This is an instance of unstable_RouterContextProvider that you use with type-safe contexts (similar to React.createContext) and is most useful with the corresponding unstable_clientMiddleware API:
import { unstable_createContext } from "react-router";
type User = {
/*...*/
};
const userContext = unstable_createContext<User>();
const sessionMiddleware: Route.unstable_ClientMiddlewareFunction = async ({
context,
}) => {
let user = await getUser();
context.set(userContext, user);
};
export const unstable_clientMiddleware = [sessionMiddleware];
export function clientLoader({ context }: Route.ClientLoaderArgs) {
let user = context.get(userContext);
let profile = await getProfile(user.id);
return { profile };
}
Similar to server-side requests, a fresh context will be created per navigation (or fetcher call). If you have initial data you'd like to populate in the context for every request, you can provide an unstable_getContext function at the root of your app:
- Library mode -
createBrowserRouter(routes, { unstable_getContext }) - Framework mode -
<HydratedRouter unstable_getContext>
This function should return an value of type unstable_InitialContext which is a Map<unstable_RouterContext, unknown> of context's and initial values:
const loggerContext = unstable_createContext<(...args: unknown[]) => void>();
function logger(...args: unknown[]) {
console.log(new Date.toISOString(), ...args);
}
function unstable_getContext() {
let map = new Map();
map.set(loggerContext, logger);
return map;
}
Middleware (unstable)
Middleware is implemented behind a future.unstable_middleware flag. To enable, you must enable the flag and the types in your react-router.config.ts file:
import type { Config } from "@react-router/dev/config";
import type { Future } from "react-router";
declare module "react-router" {
interface Future {
unstable_middleware: true; // ๐ Enable middleware types
}
}
export default {
future: {
unstable_middleware: true, // ๐ Enable middleware
},
} satisfies Config;
โ ๏ธ Middleware is unstable and should not be adopted in production. There is at least one known de-optimization in route module loading for clientMiddleware that we will be addressing this before a stable release.
โ ๏ธ Enabling middleware contains a breaking change to the context parameter passed to your loader/action functions - see below for more information.
Once enabled, routes can define an array of middleware functions that will run sequentially before route handlers run. These functions accept the same parameters as loader/action plus an additional next parameter to run the remaining data pipeline. This allows middlewares to perform logic before and after handlers execute.
// Framework mode
export const unstable_middleware = [serverLogger, serverAuth]; // server
export const unstable_clientMiddleware = [clientLogger]; // client
// Library mode
const routes = [
{
path: "/",
// Middlewares are client-side for library mode SPA's
unstable_middleware: [clientLogger, clientAuth],
loader: rootLoader,
Component: Root,
},
];
Here's a simple example of a client-side logging middleware that can be placed on the root route:
const clientLogger: Route.unstable_ClientMiddlewareFunction = async (
{ request },
next,
) => {
let start = performance.now();
// Run the remaining middlewares and all route loaders
await next();
let duration = performance.now() - start;
console.log(`Navigated to ${request.url} (${duration}ms)`);
};
Note that in the above example, the next/middleware functions don't return anything. This is by design as on the client there is no "response" to send over the network like there would be for middlewares running on the server. The data is all handled behind the scenes by the stateful router.
For a server-side middleware, the next function will return the HTTP Response that React Router will be sending across the wire, thus giving you a chance to make changes as needed. You may throw a new response to short circuit and respond immediately, or you may return a new or altered response to override the default returned by next().
const serverLogger: Route.unstable_MiddlewareFunction = async (
{ request, params, context },
next,
) => {
let start = performance.now();
// ๐ Grab the response here
let res = await next();
let duration = performance.now() - start;
console.log(`Navigated to ${request.url} (${duration}ms)`);
// ๐ And return it here (optional if you don't modify the response)
return res;
};
You can throw a redirect from a middleware to short circuit any remaining processing:
import { sessionContext } from "../context";
const serverAuth: Route.unstable_MiddlewareFunction = (
{ request, params, context },
next,
) => {
let session = context.get(sessionContext);
let user = session.get("user");
if (!user) {
session.set("returnTo", request.url);
throw redirect("/login", 302);
}
};
Note that in cases like this where you don't need to do any post-processing you don't need to call the next function or return a Response.
Here's another example of using a server middleware to detect 404s and check the CMS for a redirect:
const redirects: Route.unstable_MiddlewareFunction = async ({
request,
next,
}) => {
// attempt to handle the request
let res = await next();
// if it's a 404, check the CMS for a redirect, do it last
// because it's expensive
if (res.status === 404) {
let cmsRedirect = await checkCMSRedirects(request.url);
if (cmsRedirect) {
throw redirect(cmsRedirect, 302);
}
}
return res;
};
For more information on the middleware API/design, please see the decision doc.
Middleware context parameter
When middleware is enabled, your application will use a different type of context parameter in your loaders and actions to provide better type safety. Instead of AppLoadContext, context will now be an instance of ContextProvider that you can use with type-safe contexts (similar to React.createContext):
import { unstable_createContext } from "react-router";
import { Route } from "./+types/root";
import type { Session } from "./sessions.server";
import { getSession } from "./sessions.server";
let sessionContext = unstable_createContext<Session>();
const sessionMiddleware: Route.unstable_MiddlewareFunction = ({
context,
request,
}) => {
let session = await getSession(request);
context.set(sessionContext, session);
// ^ must be of type Session
};
// ... then in some downstream middleware
const loggerMiddleware: Route.unstable_MiddlewareFunction = ({
context,
request,
}) => {
let session = context.get(sessionContext);
// ^ typeof Session
console.log(session.get("userId"), request.method, request.url);
};
// ... or some downstream loader
export function loader({ context }: Route.LoaderArgs) {
let session = context.get(sessionContext);
let profile = await getProfile(session.get("userId"));
return { profile };
}
If you are using a custom server with a getLoadContext function, the return value for initial context values passed from the server adapter layer is no longer an object and should now return an unstable_InitialContext (Map<RouterContext, unknown>):
let adapterContext = unstable_createContext<MyAdapterContext>();
function getLoadContext(req, res): unstable_InitialContext {
let map = new Map();
map.set(adapterContext, getAdapterContext(req));
return map;
}
unstable_SerializesTo
unstable_SerializesTo added a way to register custom serialization types in Single Fetch for other library and framework authors like Apollo. It was implemented with branded type whose branded property that was made optional so that casting arbitrary values was easy:
// without the brand being marked as optional
let x1 = 42 as unknown as unstable_SerializesTo<number>;
// ^^^^^^^^^^
// with the brand being marked as optional
let x2 = 42 as unstable_SerializesTo<number>;
However, this broke type inference in loaderData and actionData for any Record types as those would now (incorrectly) match unstable_SerializesTo. This affected all users, not just those that depended on unstable_SerializesTo. To fix this, the branded property of unstable_SerializesTo is marked as required instead of optional.
For library and framework authors using unstable_SerializesTo, you may need to add as unknown casts before casting to unstable_SerializesTo.
Changes by Package
create-react-routerreact-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/serve
Full Changelog: v7.2.0...v7.3.0
v7.2.0
Date: 2025-02-18
What's Changed
Type-safe href utility
In framework mode, we now provide you with a fully type-safe href utility to give you all the warm and fuzzy feelings of path auto-completion and param validation for links in your application:
import { href } from "react-router";
export default function Component() {
const link = href("/blog/:slug", { slug: "my-first-post" });
// ^ type-safe! ^ Also type-safe!
return (
<main>
<Link to={href("/products/:id", { id: "asdf" })} />
<NavLink to={href("/:lang?/about", { lang: "en" })} />
</main>
);
}
You'll now get type errors if you pass a bad path value or a bad param value:
const badPath = href("/not/a/valid/path");
// ^ Error!
const badParam = href("/blog/:slug", { oops: "bad param" });
// ^ Error!
Prerendering with a SPA Fallback
This release enhances the ability to use a combination of pre-rendered paths alongside other paths that operate in "SPA Mode" when pre-rendering with ssr:false.
- If you specify
ssr:falsewithout aprerenderconfig, this is considered "SPA Mode" and the generatedindex.htmlfile will only render down to the root route and will be able to hydrate for any valid application path - If you specify
ssr:falsewith aprerenderconfig but do not include the/path (i.e.,prerender: ['/blog/post']), then we still generate a "SPA Mode"index.htmlfile that can hydrate for any path in the application - If you specify
ssr:falseand include the/path in yourprerenderconfig, the generatedindex.htmlfile will be specific to the root index route, so we will now also generate a separate "SPA Mode" file in__spa-fallback.htmlthat you can serve/hydrate for non-prerendered paths
For more info, see the Pre-rendering docs for more info.
Allow a root loader in SPA Mode
SPA Mode used to prohibit the use of loaders in all routes so that we could hydrate for any path in the application. However, because the root route is always rendered at build time, we can lift this restriction for the root route.
In order to use your build-time loader data during pre-rendering, we now also expose the loaderData as an optional prop for the HydrateFallback component on routes:
- This will be defined so long as the
HydrateFallbackis rendering because children routes are loading - This will be
undefinedif theHydrateFallbackis rendering because the route itself has it's own hydratingclientLoader- In SPA mode, this will allow you to render loader root data into the SPA Mode HTML file
Minor Changes
react-router- New type-safehrefutility that guarantees links point to actual paths in your app (#13012)@react-router/dev- Generate a "SPA fallback" HTML file when pre-rendering the/route withssr:false(#12948)@react-router/dev- Allow aloaderin the root route in SPA mode because it can be called/server-rendered at build time (#12948)Route.HydrateFallbackPropsnow also receivesloaderData
Patch Changes
react-router- Disable Lazy Route Discovery for allssr:falseapps and not just "SPA Mode" because there is no runtime server to serve the search-param-configured__manifestrequests (#12894)- We previously only disabled this for "SPA Mode" but we realized it should apply to all
ssr:falseapps - In those
prerenderscenarios we would pre-render the/__manifestfile but that makes some unnecessary assumptions about the static file server behaviors
- We previously only disabled this for "SPA Mode" but we realized it should apply to all
react-router- Don't apply Single Fetch revalidation de-optimization when in SPA mode since there is no server HTTP request (#12948)react-router- Properly handle revalidations to across a pre-render/SPA boundary (#13021)- In "hybrid" applications where some routes are pre-rendered and some are served from a SPA fallback, we need to avoid making
.datarequests if the path wasn't pre-rendered because the request will 404 - We don't know all the pre-rendered paths client-side, however:
- All
loaderdata inssr:falsemode is static because it's generated at build time - A route must use a
clientLoaderto do anything dynamic - Therefore, if a route only has a
loaderand not aclientLoader, we disable revalidation by default because there is no new data to retrieve - We short circuit and skip single fetch
.datarequest logic if there are no server loaders withshouldLoad=truein our single fetchdataStrategy - This ensures that the route doesn't cause a
.datarequest that would 404 after a submission
- All
- In "hybrid" applications where some routes are pre-rendered and some are served from a SPA fallback, we need to avoid making
react-router- Align dev server behavior with static file server behavior whenssr:falseis set (#12948)- When no
prerenderconfig exists, only SSR down to the rootHydrateFallback(SPA Mode) - When a
prerenderconfig exists but the current path is not pre-rendered, only SSR down to the rootHydrateFallback(SPA Fallback) - Return a 404 on
.datarequests to non-pre-rendered paths
- When no
react-router- Improve prefetch performance of CSS side effects in framework mode (#12889)react-router- Properly handle interrupted manifest requests in lazy route discovery (#12915)@react-router/dev- Handle customenvDirin Vite config (#12969)@react-router/dev- Fix CLI parsing to allow argument-lessnpx react-routerusage (#12925)@react-router/dev- Skip action-only resource routes when usingprerender:true(#13004)@react-router/dev- Enhance invalid export detection when usingssr:false(#12948)headers/actionfunctions are prohibited in all routes withssr:falsebecause there will be no runtime server on which to run themloaderfunctions are more nuanced and depend on whether a given route is prerendered- When using
ssr:falsewithout aprerenderconfig, only therootroute can have aloader - When using
ssr:falsewith aprerenderconfig, only routes matched by aprerenderpath can have aloader
- When using
@react-router/dev- Error at build time inssr:false+prerenderapps for the edge case scenario of: (#13021)- A parent route has only a
loader(does not have aclientLoader) - The parent route is pre-rendered
- The parent route has children routes which are not prerendered
- This means that when the child paths are loaded via the SPA fallback, the parent won't have any
loaderDatabecause there is no server on which to run theloader - This can be resolved by either adding a parent
clientLoaderor pre-rendering the child paths - If you add a
clientLoader, calling theserverLoader()on non-prerendered paths will throw a 404
- A parent route has only a
@react-router/dev- Limit prerendered resource route.datafiles to only the target route (#13004)@react-router/dev- Fix pre-rendering of binary files (#13039)@react-router/dev- Fix typegen for repeated params (#13012)- In React Router, path parameters are keyed by their name, so for a path pattern like
/a/:id/b/:id?/c/:id, the last:idwill set the value foridinuseParamsand theparamsprop- For example,
/a/1/b/2/c/3will result in the value{ id: 3 }at runtime
- For example,
- Previously, generated types for params incorrectly modeled repeated params with an array
- For example,
/a/1/b/2/c/3generated a type like{ id: [1,2,3] }.
- For example,
- To be consistent with runtime behavior, the generated types now correctly model the "last one wins" semantics of path parameters.
- For example,
/a/1/b/2/c/3now generates a type like{ id: 3 }.
- For example,
- In React Router, path parameters are keyed by their name, so for a path pattern like
@react-router/dev- Fix path to loadpackage.jsonforreact-router --version(#13012)
Unstable Changes
โ ๏ธ Unstable features are not recommended for production use
react-router- Addunstable_SerializesTobrand type for library authors to register types serializable by React Router's streaming format (turbo-stream) (#12264)@react-router/dev- Add unstable support for splitting route modules in framework mode viafuture.unstable_splitRouteModules(#11871)@react-router/dev- Addfuture.unstable_viteEnvironmentApiflag to enable experimental Vite Environment API support (#12936)
Split Route Modules (unstable)
โ ๏ธ This feature is currently unstable, enabled by the
future.unstable_splitRouteModulesflag. Weโd love any interested users to play with it locally and provide feedback, but we do not recommend using it in production yet.If you do choose to adopt this flag in production, please ensure you do sufficient testing against your production build to ensure that the optimization is working as expected.
One of the conveniences of the Route Module API is that everything a route needs is in a single file. Unfortunately this comes with a performance cost in some cases when using the clientLoader, clientAction, and HydrateFallback APIs.
As a basic example, consider this route module:
import { MassiveComponent } from "~/components";
export async function clientLoader() {
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
export default function Component({ loaderData }) {
return <MassiveComponent data={loaderData} />;
}
In this example we have a minimal clientLoader export that makes a basic fetch call, whereas the default component export is much larger. This is a problem for performance because it means that if we want to navigate to this route client-side, the entire route module must be downloaded before the client loader can start running.
To visualize this as a timeline:
Get Route Module: |--=======|
Run clientLoader: |-----|
Render: |-|
Instead, we want to optimize this to the following:
Get clientLoader: |--|
Get Component: |=======|
Run clientLoader: |-----|
Render: |-|
To achieve this optimization, React Router will split the route module into multiple smaller modules during the production build process. In this case, we'll end up with two separate virtual modules โ one for the client loader and one for the component and its dependencies.
export async function clientLoader() {
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
import { MassiveComponent } from "~/components";
export default function Component({ loaderData }) {
return <MassiveComponent data={loaderData} />;
}
๐ก This optimization is automatically applied in framework mode, but you can also implement it in library mode via
route.lazyand authoring your route in multiple files as covered in our blog post on lazy loading route modules.
Now that these are available as separate modules, the client loader and the component can be downloaded in parallel. This means that the client loader can be executed as soon as it's ready without having to wait for the component.
This optimization is even more pronounced when more Route Module APIs are used. For example, when using clientLoader, clientAction and HydrateFallback, the timeline for a single route module during a client-side navigation might look like this:
Get Route Module: |--~~++++=======|
Run clientLoader: |-----|
Render: |-|
This would instead be optimized to the following:
Get clientLoader: |--|
Get clientAction: |~~|
Get HydrateFallback: SKIPPED
Get Component: |=======|
Run clientLoader: |-----|
Render: |-|
Note that this optimization only works when the Route Module APIs being split don't share code within the same file. For example, the following route module can't be split:
import { MassiveComponent } from "~/components";
const shared = () => console.log("hello");
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
export default function Component({ loaderData }) {
shared();
return <MassiveComponent data={loaderData} />;
}
This route will still work, but since both the client loader and the component depend on the shared function defined within the same file, it will be de-optimized into a single route module.
To avoid this, you can extract any code shared between exports into a separate file. For example:
export const shared = () => console.log("hello");
You can then import this shared code in your route module without triggering the de-optimization:
import { MassiveComponent } from "~/components";
import { shared } from "./shared";
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
export default function Component({ loaderData }) {
shared();
return <MassiveComponent data={loaderData} />;
}
Since the shared code is in its own module, React Router is now able to split this route module into two separate virtual modules:
import { shared } from "./shared";
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
import { MassiveComponent } from "~/components";
import { shared } from "./shared";
export default function Component({ loaderData }) {
shared();
return <MassiveComponent data={loaderData} />;
}
If your project is particularly performance sensitive, you can set the unstable_splitRouteModules future flag to "enforce":
export default {
future: {
unstable_splitRouteModules: "enforce",
},
};
This setting will raise an error if any route modules can't be split:
Error splitting route module: routes/example/route.tsx
- clientLoader
This export could not be split into its own chunk because it shares code with other exports. You should extract any shared code into its own module and then import it within the route module.
Changes by Package
create-react-routerreact-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/serve
Full Changelog: v7.1.5...v7.2.0
v7.1.5
Date: 2025-01-31
Patch Changes
react-router- Fix regression introduced in7.1.4via #12800 that caused issues navigating to hash routes inside splat routes for applications using Lazy Route Discovery (patchRoutesOnNavigation) (#12927)
Full Changelog: v7.1.4...v7.1.5
v7.1.4
Date: 2025-01-30
Patch Changes
@react-router/dev- Properly resolve Windows file paths to scan for Vite's dependency optimization when using theunstable_optimizeDepsfuture flag (#12637)@react-router/dev- Fix prerendering when using a custom server - previously we ended up trying to import the users custom server when we actually want to import the virtual server build module (#12759)react-router- Properly handle status codes that cannot have a body in single fetch responses (204, etc.) (#12760)react-router- Properly bubble headers aserrorHeaderswhen throwing adata()result (#12846)- Avoid duplication of
Set-Cookieheaders if also returned fromheaders
- Avoid duplication of
react-router- Stop erroring on resource routes that return raw strings/objects and instead serialize them astext/plainorapplication/jsonresponses (#12848)- This only applies when accessed as a resource route without the
.dataextension - When accessed from a Single Fetch
.datarequest, they will still be encoded viaturbo-stream
- This only applies when accessed as a resource route without the
react-router- Optimize Lazy Route Discovery path discovery to favor a singlequerySelectorAllcall at thebodylevel instead of many calls at the sub-tree level (#12731)react-router- Optimize route matching by skipping redundantmatchRoutescalls when possible (#12800, #12882)react-router- Internal reorg to clean up some duplicated route module types (#12799)
Full Changelog: v7.1.3...v7.1.4
v7.1.3
Date: 2025-01-17
Patch Changes
@react-router/dev- FixrevealandroutesCLI commands (#12745)
Full Changelog: v7.1.2...v7.1.3
v7.1.2
Date: 2025-01-16
Patch Changes
react-router- Fix issue with fetcher data cleanup in the data layer on fetcher unmount (#12681)react-router- Do not rely onsymbolfor filtering outredirectresponses from loader data (#12694)- Previously, some projects were getting type checking errors like:
error TS4058: Return type of exported function has or is using name 'redirectSymbol' from external module "node_modules/..." but cannot be named. - Now that
symbols are not used for theredirectresponse type, these errors should no longer be present
- Previously, some projects were getting type checking errors like:
@react-router/dev- Fix default external conditions in Vite v6 (#12644)- This fixes resolution issues with certain npm packages
@react-router/dev- Fix mismatch in prerendering html/data files when path is missing a leading slash (#12684)@react-router/dev- Usemodule-syncserver condition when enabled in the runtime. This fixes React context mismatches (e.g.useHref() may be used only in the context of a <Router> component.) during development on Node 22.10.0+ when using libraries that have a peer dependency on React Router (#12729)@react-router/dev- Fixreact-refreshsource maps (#12686)
Full Changelog: v7.1.1...v7.1.2
v7.1.1
Date: 2024-12-23
Patch Changes
@react-router/dev- Fix for a crash when optional args are passed to the CLI (#12609)
Full Changelog: v7.1.0...v7.1.1
v7.1.0
Date: 2024-12-20
Minor Changes
- Add support for Vite v6 (#12469)
Patch Changes
react-router- Throw unwrapped Single Fetchredirectto align with pre-Single Fetch behavior (#12506)react-router- Ignore redirects when inferring loader data types (#12527)react-router- Remove<Link prefetch>warning which suffers from false positives in a lazy route discovery world (#12485)create-react-router- Fix missingfs-extradependency (#12556)@react-router/dev/@react-router/serve- Properly initializeNODE_ENVif not already set for compatibility with React 19 (#12578)@react-router/dev- Remove the leftover/unusedabortDelayprop fromServerRouterand update the defaultentry.server.tsxto use the newstreamTimeoutvalue for Single Fetch (#12478)- The
abortDelayfunctionality was removed in v7 as it was coupled to thedeferimplementation from Remix v2, but this removal of this prop was missed - If you were still using this prop in your
entry.serverfile, it's likely your app is not aborting streams as you would expect and you will need to adopt the newstreamTimeoutvalue introduced with Single Fetch
- The
@react-router/fs-routes- Throw error inflatRoutesif routes directory is missing (#12407)
Changes by Package
create-react-routerreact-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/serve
Full Changelog: v7.0.2...v7.1.0
v7.0.2
Date: 2024-12-02
Patch Changes
react-router- Temporarily only use one build in export map so packages can have a peer dependency on react router (#12437)@react-router/dev- SupportmoduleResolutionNode16andNodeNext(#12440)@react-router/dev- Generate widematchesandparamstypes for child routes (#12397)- At runtime,
matchesincludes child route matches andparamsinclude child route path parameters - But previously, we only generated types for parent routes and the current route in
matchesandparams - To align our generated types more closely to the runtime behavior, we now generate more permissive, wider types when accessing child route information
- At runtime,
Full Changelog: v7.0.1...v7.0.2
v7.0.1
Date: 2024-11-22
Patch Changes
@react-router/dev- Ensure typegen file watcher is cleaned up when Vite dev server restarts (#12331)@react-router/dev- Pass routeerrortoErrorBoundaryas a prop (#12338)
Full Changelog: v7.0.0...v7.0.1
v7.0.0
Date: 2024-11-21
Breaking Changes
Package Restructuring
- The
react-router-dom,@remix-run/react,@remix-run/server-runtime, and@remix-run/routerhave been collapsed into thereact-routerpackage- To ease migration,
react-router-domis still published in v7 as a re-export of everything fromreact-router
- To ease migration,
- The
@remix-run/cloudflare-pagesand@remix-run/cloudflare-workershave been collapsed into@react-router/cloudflarepackage` - The
react-router-dom-v5-compatandreact-router-nativepackages are removed starting with v7
Removed Adapter Re-exports
Remix 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";
Removed APIs
The following APIs have been removed in React Router v7:
jsondeferunstable_composeUploadHandlersunstable_createMemoryUploadHandlerunstable_parseMultipartFormData
Minimum Versions
React Router v7 requires the following minimum versions:
node@20- React Router no longer provides an
installGlobalsmethod to polyfill thefetchAPI
- React Router no longer provides an
react@18,react-dom@18
Adopted Future Flag Behaviors
Remix 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:
- React Router v6 flags
future.v7_relativeSplatPathfuture.v7_startTransitionfuture.v7_fetcherPersistfuture.v7_normalizeFormMethodfuture.v7_partialHydrationfuture.v7_skipActionStatusRevalidation
- Remix v2 flags
future.v3_fetcherPersistfuture.v3_relativeSplatPathfuture.v3_throwAbortReasonfuture.v3_singleFetchfuture.v3_lazyRouteDiscoveryfuture.v3_optimizeDeps
Vite Compiler
The 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;
Exposed Router Promises
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()
Other Notable Changes
routes.ts
When 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"),
+ ]),
+];
Type-safety improvements
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.
Prerendering
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"];
}
Major Changes (react-router)
- Remove the original
deferimplementation in favor of using raw promises via single fetch andturbo-stream(#11744)- This removes these exports from React Router:
deferAbortedDeferredErrortype TypedDeferredDataUNSAFE_DeferredDataUNSAFE_DEFERRED_SYMBOL
- This removes these exports from React Router:
- Collapse packages into
react-router(#11505)@remix-run/routerreact-router-dom@remix-run/server-runtime@remix-run/testing- As a note, the
react-router-dompackage is maintained to ease adoption but it simply re-exports all APIs fromreact-router
- Drop support for Node 16, React Router SSR now requires Node 18 or higher (#11391, #11690)
- Remove
future.v7_startTransitionflag (#11696) - Expose the underlying router promises from the following APIs for composition in React 19 APIs: (#11521)
- Remove
future.v7_normalizeFormMethodfuture flag (#11697) - Imports/Exports cleanup (#11840)
- Removed the following exports that were previously public API from
@remix-run/router- types
AgnosticDataIndexRouteObjectAgnosticDataNonIndexRouteObjectAgnosticDataRouteMatchAgnosticDataRouteObjectAgnosticIndexRouteObjectAgnosticNonIndexRouteObjectAgnosticRouteMatchAgnosticRouteObjectTrackedPromiseunstable_AgnosticPatchRoutesOnMissFunctionAction-> exported asNavigationTypeviareact-routerRouterexported asRemixRouterto differentiate from RR's<Router>
- API
getToPathname(@private)joinPaths(@private)normalizePathname(@private)resolveTo(@private)stripBasename(@private)createBrowserHistory-> in favor ofcreateBrowserRoutercreateHashHistory-> in favor ofcreateHashRoutercreateMemoryHistory-> in favor ofcreateMemoryRoutercreateRoutercreateStaticHandler-> in favor of wrappercreateStaticHandlerin RR DomgetStaticContextFromError
- types
- Removed the following exports that were previously public API from
react-routerHashPathnameSearch
- Removed the following exports that were previously public API from
- Remove
future.v7_prependBasenamefrom the internalized@remix-run/routerpackage (#11726) - Remove
future.v7_throwAbortReasonfrom internalized@remix-run/routerpackage (#11728) - Add
exportsfield to all packages (#11675) - Renamed
RemixContexttoFrameworkContext(#11705) - Update the minimum React version to 18 (#11689)
PrefetchPageDescriptorreplaced byPageLinkDescriptor(#11960)- Remove the
future.v7_partialHydrationflag (#11725)- This also removes the
<RouterProvider fallbackElement>prop- To migrate, move the
fallbackElementto ahydrateFallbackElement/HydrateFallbackon your root route
- To migrate, move the
- Also worth nothing there is a related breaking changer with this future flag:
- Without
future.v7_partialHydration(when usingfallbackElement),state.navigationwas populated during the initial load - With
future.v7_partialHydration,state.navigationremains in an"idle"state during the initial load
- Without
- This also removes the
- Remove
future.v7_relativeSplatPathfuture flag (#11695) - Remove remaining future flags (#11820)
- React Router
v7_skipActionErrorRevalidation - Remix
v3_fetcherPersist,v3_relativeSplatPath,v3_throwAbortReason
- React Router
- Rename
createRemixStubtocreateRoutesStub(#11692) - Remove
@remix-run/routerdeprecateddetectErrorBoundaryoption in favor ofmapRouteProperties(#11751) - Add
react-router/domsubpath export to properly enablereact-domas an optionalpeerDependency(#11851)- This ensures that we don't blindly
import ReactDOM from "react-dom"in<RouterProvider>in order to accessReactDOM.flushSync(), since that would breakcreateMemoryRouteruse cases in non-DOM environments - DOM environments should import from
react-router/domto get the proper component that makesReactDOM.flushSync()available:- If you are using the Vite plugin, use this in your
entry.client.tsx:import { HydratedRouter } from 'react-router/dom'
- If you are not using the Vite plugin and are manually calling
createBrowserRouter/createHashRouter:import { RouterProvider } from "react-router/dom"
- If you are using the Vite plugin, use this in your
- This ensures that we don't blindly
- Remove
future.v7_fetcherPersistflag (#11731) - Allow returning
undefinedfrom loaders and actions (#11680, #12057) - Use
createRemixRouter/RouterProviderinentry.clientinstead ofRemixBrowser(#11469) - Remove the deprecated
jsonutility (#12146)- You can use
Response.jsonif you still need to construct JSON responses in your app
- You can use
Major Changes (@react-router/*)
- Remove
future.v3_singleFetchflag (#11522) - Drop support for Node 16 and 18, update minimum Node version to 20 (#11690, #12171)
- Remove
installGlobals()as this should no longer be necessary
- Remove
- Add
exportsfield to all packages (#11675) - No longer re-export APIs from
react-routerthrough different runtime/adapter packages (#11702) - For Remix consumers migrating to React Router, the
cryptoglobal from the Web Crypto API is now required when using cookie and session APIs- This means that the following APIs are provided from
react-routerrather than platform-specific packages: (#11837)createCookiecreateCookieSessionStoragecreateMemorySessionStoragecreateSessionStorage
- For consumers running older versions of Node, the
installGlobalsfunction from@remix-run/nodehas been updated to defineglobalThis.crypto, using Node'srequire('node:crypto').webcryptoimplementation - Since platform-specific packages no longer need to implement this API, the following low-level APIs have been removed:
createCookieFactorycreateSessionStorageFactorycreateCookieSessionStorageFactorycreateMemorySessionStorageFactory
- This means that the following APIs are provided from
- Consolidate types previously duplicated across
@remix-run/router,@remix-run/server-runtime, and@remix-run/reactnow that they all live inreact-router(#12177)- Examples:
LoaderFunction,LoaderFunctionArgs,ActionFunction,ActionFunctionArgs,DataFunctionArgs,RouteManifest,LinksFunction,Route,EntryRoute - The
RouteManifesttype used by the "remix" code is now slightly stricter because it is using the former@remix-run/routerRouteManifestRecord<string, Route> -> Record<string, Route | undefined>
- Removed
AppDatatype in favor of inliningunknownin the few locations it was used - Removed
ServerRuntimeMeta*types in favor of theMeta*types they were duplicated from
- Examples:
- Migrate Remix v2 type generics to React Router (#12180)
- These generics are provided for Remix v2 migration purposes
- These generics and the APIs they exist on should be considered informally deprecated in favor of the new
Route.*types - Anyone migrating from React Router v6 should probably not leverage these new generics and should migrate straight to the
Route.*types - For React Router v6 users, these generics are new and should not impact your app, with one exception
useFetcherpreviously had an optional generic (used primarily by Remix v2) that expected the data type- This has been updated in v7 to expect the type of the function that generates the data (i.e.,
typeof loader/typeof action) - Therefore, you should update your usages:
- โ
useFetcher<LoaderData>() - โ
useFetcher<typeof loader>()
- โ
- Update
cookiedependency 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-pagesare now provided for React Router consumers in the@react-router/cloudflarepackage. There is no longer a separate package for Cloudflare Pages. (#11801)@react-router/cloudflare- The@remix-run/cloudflare-workerspackage has been deprecated. Remix consumers migrating to React Router should use the@react-router/cloudflarepackage directly. For guidance on how to use@react-router/cloudflarewithin a Cloudflare Workers context, refer to the Cloudflare Workers template. (#11801)@react-router/dev- For Remix consumers migrating to React Router, thevitePluginandcloudflareDevProxyVitePluginexports have been renamed and moved. (#11904)@react-router/dev- For Remix consumers migrating to React Router who used the Vite plugin'sbuildEndhook, the resolvedreactRouterConfigobject no longer contains apublicPathproperty since this belongs to Vite, not React Router (#11575)@react-router/dev- For Remix consumers migrating to React Router, the Vite plugin'smanifestoption has been removed (#11573)@react-router/dev- Update defaultisbotversion to v5 and drop support forisbot@3(#11770)- If you have
isbot@4orisbot@5in yourpackage.json:- You do not need to make any changes
- If you have
isbot@3in yourpackage.jsonand you have your ownentry.server.tsxfile in your repo- You do not need to make any changes
- You can upgrade to
isbot@5independent of the React Router v7 upgrade
- If you have
isbot@3in yourpackage.jsonand you do not have your ownentry.server.tsxfile in your repo- You are using the internal default entry provided by React Router v7 and you will need to upgrade to
isbot@5in yourpackage.json
- You are using the internal default entry provided by React Router v7 and you will need to upgrade to
- If you have
@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.jsonandbuild/server/.vite/manifest.jsoninstead ofbuild/.vite/client-manifest.jsonandbuild/.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)- Originally the Remix Vite plugin moved all Vite manifests to a root-level
build/.vitedirectory 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'sbuild.manifesthad 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.
- Originally the Remix Vite plugin moved all Vite manifests to a root-level
Minor Changes
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 duplicateRouterProviderimplementations (#11679)react-router- Stabilizeunstable_dataStrategy(#11969)react-router- Stabilizeunstable_patchRoutesOnNavigation(#11970)react-router- Add prefetching support toLink/NavLinkwhen using Remix SSR (#11402)react-router- EnhanceScrollRestorationso it can restore properly on an SSR'd document load (#11401)@react-router/dev- Add support for theprerenderconfig in the React Router vite plugin, to support existing SSG use-cases (#11539)@react-router/dev- Remove internalentry.server.spa.tsximplementation which was not compatible with the Single Fetch async hydration approach (#11681)@react-router/serve: Updateexpress.staticconfigurations to support newprerenderAPI (#11547)- Assets in the
build/client/assetsfolder are served as before, with a 1-year immutableCache-Controlheader - Static files outside of assets, such as pre-rendered
.htmland.datafiles are not served with a specificCache-Controlheader .datafiles are served withContent-Type: text/x-turbo- For some reason, when adding this via
express.static, it seems to also add aCache-Control: public, max-age=0to.datafiles
- For some reason, when adding this via
- Assets in the
Patch Changes
- Replace
substrwithsubstring(#12080) react-router- Fix redirects returned from loaders/actions usingdata()(#12021)@react-router/dev- Enable prerendering for resource routes (#12200)@react-router/dev- resolve config directory relative to flat output file structure (#12187)
Changes by Package
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/serve
Full Changelog: v6.28.0...v7.0.0
React Router v6 Releases
v6.30.3
Date: 2026-01-07
Security Notice
This release addresses 1 security vulnerability:
Patch Changes
- Validate redirect locations (#14707)
Full Changelog: v6.30.2...v6.30.3
v6.30.2
Date: 2025-11-13
Security Notice
This release addresses 1 security vulnerability:
Patch Changes
- Normalize double-slashes in
resolvePath(#14537)
Full Changelog: v6.30.1...v6.30.2
v6.30.1
Date: 2025-05-20
Patch Changes
- Partially revert optimization added in
6.29.0to reduce calls tomatchRoutesbecause it surfaced other issues (#13623) - Stop logging invalid warning when
v7_relativeSplatPathis set tofalse(#13502)
Full Changelog: v6.30.0...v6.30.1
v6.30.0
Date: 2025-02-27
Minor Changes
- Add
fetcherKeyas a parameter topatchRoutesOnNavigation(#13109)
Patch Changes
- Fix regression introduced in
6.29.0via #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
v6.29.0
Date: 2025-01-30
Minor Changes
- Provide the request
signalas a parameter topatchRoutesOnNavigation(#12900)- This can be used to abort any manifest fetches if the in-flight navigation/fetcher is aborted
Patch Changes
- Do not log v7 deprecation warnings in production builds (#12794)
- Properly bubble headers when throwing a
data()result (#12845) - Optimize route matching by skipping redundant
matchRoutescalls when possible (#12169) - Strip search parameters from
patchRoutesOnNavigationpathparam for fetcher calls (#12899)
Full Changelog: v6.28.2...v6.29.0
v6.28.2
Date: 2025-01-16
Patch Changes
- Fix manual fetcher
keyusage when not opted intofuture.v7_fetcherPersist(#12674) - Fix issue with fetcher data cleanup in the data layer on fetcher unmount (#12674)
Full Changelog: v6.28.1...v6.28.2
v6.28.1
Date: 2024-12-20
Patch Changes
- Allow users to opt out of v7 deprecation warnings by setting flags to
false(#12441)
Full Changelog: v6.28.0...v6.28.1
v6.28.0
Date: 2024-11-06
What's Changed
- In preparation for v7 we've added deprecation warnings for any future flags that you have not yet opted into. Please use the flags to better prepare for eventually upgrading to v7.
Minor Changes
- Log deprecation warnings for v7 flags (#11750)
- Add deprecation warnings to
json/deferin favor of returning raw objects- These methods will be removed in React Router v7
- Add deprecation warnings to
Patch Changes
- Update JSDoc URLs for new website structure (add /v6/ segment) (#12141)
Full Changelog: v6.27.0...v6.28.0
v6.27.0
Date: 2024-10-11
What's Changed
Stabilized APIs
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(createBrowserRouterand friends) (Docs)unstable_patchRoutesOnNavigationโpatchRoutesOnNavigation(createBrowserRouterand friends) (Docs)unstable_flushSyncโflushSync(useSubmit,fetcher.load,fetcher.submit) (Docs)unstable_viewTransitionโviewTransition(<Link>,<Form>,useNavigate,useSubmit) (Docs)
Minor Changes
- Stabilize the
unstable_flushSyncoption for navigations and fetchers (#11989) - Stabilize the
unstable_viewTransitionoption for navigations and the correspondingunstable_useViewTransitionStatehook (#11989) - Stabilize
unstable_dataStrategy(#11974) - Stabilize
unstable_patchRoutesOnNavigation(#11973)- Add new
PatchRoutesOnNavigationFunctionArgstype for convenience (#11967)
- Add new
Patch Changes
- Fix bug when submitting to the current contextual route (parent route with an index child) when an
?indexparam already exists from a prior submission (#12003) - Fix
useFormActionbug - when removing?indexparam it would not keep other non-Remixindexparams (#12003) - Fix bug with fetchers not persisting
preventScrollResetthrough redirects during concurrent fetches (#11999) - Avoid unnecessary
console.erroron fetcher abort due to back-to-back revalidation calls (#12050) - Fix bugs with
partialHydrationwhen hydrating with errors (#12070) - Remove internal cache to fix issues with interrupted
patchRoutesOnNavigationcalls (#12055)- โ ๏ธ This may be a breaking change if you were relying on this behavior in the
unstable_API - We used to cache in-progress calls to
patchRoutesOnNavigationinternally so that multiple navigations with the same start/end would only execute the function once and use the same promise - However, this approach was at odds with
patchshort circuiting if a navigation was interrupted (and therequest.signalaborted) since the first invocation'spatchwould no-op - This cache also made some assumptions as to what a valid cache key might be - and is oblivious to any other application-state changes that may have occurred
- So, the cache has been removed because in most cases, repeated calls to something like
import()for async routes will already be cached automatically - and if not it's easy enough for users to implement this cache in userland
- โ ๏ธ This may be a breaking change if you were relying on this behavior in the
- Remove internal
discoveredRoutesFIFO queue fromunstable_patchRoutesOnNavigation(#11977)- โ ๏ธ This may be a breaking change if you were relying on this behavior in the
unstable_API - This was originally implemented as an optimization but it proved to be a bit too limiting
- If you need this optimization you can implement your own cache inside
patchRoutesOnNavigation
- โ ๏ธ This may be a breaking change if you were relying on this behavior in the
- Fix types for
RouteObjectwithinPatchRoutesOnNavigationFunction'spatchmethod so it doesn't expect agnostic route objects passed topatch(#11967) - Expose errors thrown from
patchRoutesOnNavigationdirectly touseRouteErrorinstead of wrapping them in a 400ErrorResponseinstance (#12111)
Full Changelog: v6.26.2...v6.27.0
v6.26.2
Date: 2024-09-09
Patch Changes
- Update the
unstable_dataStrategyAPI to allow for more advanced implementations (#11943)- โ ๏ธ If you have already adopted
unstable_dataStrategy, please review carefully as this includes breaking changes to this API - Rename
unstable_HandlerResulttounstable_DataStrategyResult - Change the return signature of
unstable_dataStrategyfrom a parallel array ofunstable_DataStrategyResult[](parallel tomatches) to a key/value object ofrouteId => unstable_DataStrategyResult- This allows more advanced control over revalidation behavior because you can opt-into or out-of revalidating data that may not have been revalidated by default (via
match.shouldLoad)
- This allows more advanced control over revalidation behavior because you can opt-into or out-of revalidating data that may not have been revalidated by default (via
- You should now return/throw a result from your
handlerOverrideinstead of returning aDataStrategyResult- The return value (or thrown error) from your
handlerOverridewill be wrapped up into aDataStrategyResultand returned frommmatch.resolve - Therefore, if you are aggregating the results of
match.resolve()into a final results object you should not need to think about theDataStrategyResulttype - If you are manually filling your results object from within your
handlerOverride, then you will need to assign aDataStrategyResultas the value so React Router knows if it's a successful execution or an error (see examples in the documentation for details)
- The return value (or thrown error) from your
- Added a new
fetcherKeyparameter tounstable_dataStrategyto allow differentiation from navigational and fetcher calls
- โ ๏ธ If you have already adopted
- Preserve opted-in view transitions through redirects (#11925)
- Preserve pending view transitions through a router revalidation call (#11917)
- Fix blocker usage when
blocker.proceedis called quickly/synchronously (#11930)
Full Changelog: v6.26.1...v6.26.2
v6.26.1
Date: 2024-08-15
Patch Changes
- Rename
unstable_patchRoutesOnMisstounstable_patchRoutesOnNavigationto match new behavior (#11888) - Update
unstable_patchRoutesOnNavigationlogic 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)- We also now leverage an internal FIFO queue of previous paths we've already called
unstable_patchRoutesOnNavigationagainst so that we don't re-call on subsequent navigations to the same path
- We also now leverage an internal FIFO queue of previous paths we've already called
Full Changelog: v6.26.0...v6.26.1
v6.26.0
Date: 2024-08-01
Minor Changes
- Add a new
replace(url, init?)alternative toredirect(url, init?)that performs ahistory.replaceStateinstead of ahistory.pushStateon client-side navigation redirects (#11811) - Add a new
unstable_data()API for usage with Remix Single Fetch (#11836)- This API is not intended for direct usage in React Router SPA applications
- It is primarily intended for usage with
createStaticHandler.query()to allow loaders/actions to return arbitrary data along with customstatus/headerswithout forcing the serialization of data into aResponseinstance - This allows for more advanced serialization tactics via
unstable_dataStrategysuch as serializing viaturbo-streamin Remix Single Fetch - โ ๏ธ This removes the
statusfield fromHandlerResult- If you need to return a specific
statusfromunstable_dataStrategyyou should instead do so viaunstable_data()
- If you need to return a specific
Patch Changes
- Fix internal cleanup of interrupted fetchers to avoid invalid revalidations on navigations (#11839)
- Fix initial hydration behavior when using
future.v7_partialHydrationalong withunstable_patchRoutesOnMiss(#11838)- During initial hydration,
router.state.matcheswill now include any partial matches so that we can render ancestorHydrateFallbackcomponents
- During initial hydration,
Full Changelog: v6.25.1...v6.26.0
v6.25.1
Date: 2024-07-17
Patch Changes
- Memoize some
RouterProviderinternals to reduce unnecessary re-renders (#11803)
Full Changelog: v6.25.0...v6.25.1
v6.25.0
Date: 2024-07-16
What's Changed
Stabilized v7_skipActionErrorRevalidation
This release stabilizes the future.unstable_skipActionErrorRevalidation flag into future.v7_skipActionErrorRevalidation in preparation for the upcoming React Router v7 release.
- When this flag is enabled, actions that return/throw a
4xx/5xxResponsewill not trigger a revalidation by default - This also stabilizes
shouldRevalidate'sunstable_actionStatusparameter toactionStatus
Minor Changes
- Stabilize
future.unstable_skipActionErrorRevalidationasfuture.v7_skipActionErrorRevalidation(#11769)
Patch Changes
- Fix regression and properly decode paths inside
useMatchso matches/params reflect decoded params (#11789) - Fix bubbling of errors thrown from
unstable_patchRoutesOnMiss(#11786) - Fix hydration in SSR apps using
unstable_patchRoutesOnMissthat matched a splat route on the server (#11790)
Full Changelog: v6.24.1...v6.25.0
v6.24.1
Date: 2024-07-03
Patch Changes
- Remove
polyfill.ioreference from warning message because the domain was sold and has since been determined to serve malware (#11741) - Export
NavLinkRenderPropstype for easier typing of customNavLinkcallback (#11553) - When using
future.v7_relativeSplatPath, properly resolve relative paths in splat routes that are children of pathless routes (#11633) - Fog of War (unstable): Trigger a new
router.routesidentity/reflow during route patching (#11740) - Fog of War (unstable): Fix initial matching when a splat route matches (#11759)
Full Changelog: v6.24.0...v6.24.1
v6.24.0
Date: 2024-06-24
What's Changed
Lazy Route Discovery (a.k.a. "Fog of War")
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]);
}
},
},
);
Minor Changes
- Add support for Lazy Route Discovery (a.k.a. "Fog of War") (#11626)
Patch Changes
- Fix
fetcher.submittypes - remove incorrectnavigate/fetcherKey/unstable_viewTransitionoptions because they are only relevant foruseSubmit(#11631) - Allow falsy
location.statevalues passed to<StaticRouter>(#11495)
Full Changelog: v6.23.1...v6.24.0
v6.23.1
Date: 2024-05-10
Patch Changes
- Allow
undefinedto be resolved through<Await>(#11513) - Add defensive
documentcheck when checking fordocument.startViewTransitionavailability (#11544) - Change the
react-router-dom/serverimport back toreact-router-dominstead ofindex.ts(#11514) @remix-run/router- Supportunstable_dataStrategyonstaticHandler.queryRoute(#11515)
Full Changelog: v6.23.0...v6.23.1
v6.23.0
Date: 2024-04-23
What's Changed
Data Strategy (unstable)
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.
Skip Action Error Revalidation (unstable)
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.
Minor Changes
- Add a new
unstable_dataStrategyconfiguration option (#11098, #11377) @remix-run/router- Add a newfuture.unstable_skipActionRevalidationfuture flag (#11098)@remix-run/router- SSR: Added a newskipLoaderErrorBubblingoptions to thestaticHandler.querymethod 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
v6.22.3
Date: 2024-03-07
Patch Changes
- Fix a
future.v7_partialHydrationbug that would re-run loaders below the boundary on hydration if SSR loader errors bubbled to a parent boundary (#11324) - Fix a
future.v7_partialHydrationbug that would consider the router uninitialized if a route did not have a loader (#11325)
Full Changelog: v6.22.2...v6.22.3
v6.22.2
Date: 2024-02-28
Patch Changes
- Preserve hydrated errors during partial hydration runs (#11305)
Full Changelog: v6.22.1...v6.22.2
v6.22.1
Date: 2024-02-16
Patch Changes
- Fix encoding/decoding issues with pre-encoded dynamic parameter values (#11199)
Full Changelog: v6.22.0...v6.22.1
v6.22.0
Date: 2024-02-01
What's Changed
Core Web Vitals Technology Report Flag
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.
Minor Changes
- Include a
window.__reactRouterVersionfor CWV Report detection (#11222) - Add a
createStaticHandlerfuture.v7_throwAbortReasonflag to throwrequest.signal.reason(defaults to aDOMException) when a request is aborted instead of anErrorsuch asnew Error("query() call aborted: GET /path")(#11104)- Please note that
DOMExceptionwas added in Node v17 so you will not get aDOMExceptionon Node 16 and below.
- Please note that
Patch Changes
- Respect the
ErrorResponsestatus code if passed togetStaticContextFormError(#11213)
Full Changelog: v6.21.3...v6.22.0
v6.21.3
Date: 2024-01-18
Patch Changes
- Fix
NavLinkisPendingwhen abasenameis used (#11195) - Remove leftover
unstable_prefix fromBlocker/BlockerFunctiontypes (#11187)
Full Changelog: v6.21.2...v6.21.3
v6.21.2
Date: 2024-01-11
Patch Changes
- Leverage
useIdfor internal fetcher keys when available (#11166) - Fix bug where dashes were not picked up in dynamic parameter names (#11160)
- Do not attempt to deserialize empty JSON responses (#11164)
Full Changelog: v6.21.1...v6.21.2
v6.21.1
Date: 2023-12-21
Patch Changes
- Fix bug with
route.lazynot working correctly on initial SPA load whenv7_partialHydrationis specified (#11121) - Fix bug preventing revalidation from occurring for persisted fetchers unmounted during the
submittingphase (#11102) - De-dup relative path logic in
resolveTo(#11097)
Full Changelog: v6.21.0...v6.21.1
v6.21.0
Date: 2023-12-13
What's Changed
future.v7_relativeSplatPath
We fixed a splat route path-resolution bug in 6.19.0, but later determined a large number of applications were relying on the buggy behavior, so we reverted the fix in 6.20.1 (see #10983, #11052, #11078).
The buggy behavior is that the default behavior when resolving relative paths inside a splat route would ignore any splat (*) portion of the current route path. When the future flag is enabled, splat portions are included in relative path logic within splat routes.
For more information, please refer to the useResolvedPath docs and/or the detailed changelog entry.
Partial Hydration
We added a new future.v7_partialHydration future flag for the @remix-run/router that enables partial hydration of a data router when Server-Side Rendering. This allows you to provide hydrationData.loaderData that has values for some initially matched route loaders, but not all. When this flag is enabled, the router will call loader functions for routes that do not have hydration loader data during router.initialize(), and it will render down to the deepest provided HydrateFallback (up to the first route without hydration data) while it executes the unhydrated routes. (#11033)
Minor Changes
- Add a new
future.v7_relativeSplatPathflag to implement a breaking bug fix to relative routing when inside a splat route. (#11087) - Add a new
future.v7_partialHydrationfuture flag that enables partial hydration of a data router when Server-Side Rendering (#11033)
Patch Changes
- Properly handle falsy error values in
ErrorBoundary's (#11071) - Catch and bubble errors thrown when trying to unwrap responses from
loader/actionfunctions (#11061) - Fix
relative="path"issue when renderingLink/NavLinkoutside of matched routes (#11062)
Full Changelog: v6.20.1...v6.21.0
v6.20.1
Date: 2023-12-01
Patch Changes
- Revert the
useResolvedPathfix for splat routes due to a large number of applications that were relying on the buggy behavior (see #11052) (#11078)- We plan to re-introduce this fix behind a future flag in the next minor version (see this comment)
- This fix was included in versions
6.19.0and6.20.0. If you are upgrading from6.18.0or earlier, you would not have been impacted by this fix.
Full Changelog: v6.20.0...v6.20.1
v6.20.0
Date: 2023-11-22
Warning
Please use version 6.20.1 or later instead of 6.20.0. We discovered that a large number of apps were relying on buggy behavior that was fixed in this release (#11045). We reverted the fix in 6.20.1 and will be re-introducing it behind a future flag in a subsequent release. See #11052 for more details.
Minor Changes
- Export the
PathParamtype from the public API (#10719)
Patch Changes
- Do not revalidate unmounted fetchers when
v7_fetcherPersistis enabled (#11044) - Fix bug with
resolveTopath resolution in splat routes (#11045)- This is a follow up to #10983 to handle the few other code paths using
getPathContributingMatches - This removes the
UNSAFE_getPathContributingMatchesexport from@remix-run/routersince we no longer need this in thereact-router/react-router-domlayers
- This is a follow up to #10983 to handle the few other code paths using
Full Changelog: v6.19.0...v6.20.0
v6.19.0
Date: 2023-11-16
Warning
Please use version 6.20.1 or later instead of 6.19.0. We discovered that a large number of apps were relying on buggy behavior that was fixed in this release (#10983). We reverted the fix in 6.20.1 and will be re-introducing it behind a future flag in a subsequent release. See #11052 for more details.
What's Changed
unstable_flushSync API
This release brings a new unstable_flushSync option to the imperative APIs (useSubmit, useNavigate, fetcher.submit, fetcher.load) to let users opt-into synchronous DOM updates for pending/optimistic UI.
function handleClick() {
submit(data, { flushSync: true });
// Everything is flushed to the DOM so you can focus/scroll to your pending/optimistic UI
setFocusAndOrScrollToNewlyAddedThing();
}
Minor Changes
- Add
unstable_flushSyncoption touseNavigate/useSubmit/fetcher.load/fetcher.submitto opt-out ofReact.startTransitionand intoReactDOM.flushSyncfor state updates (#11005) - Remove the
unstable_prefix from theuseBlockerhook as it's been in use for enough time that we are confident in the API (#10991)- We do not plan to remove the prefix from
unstable_usePromptdue to differences in how browsers handlewindow.confirmthat prevent React Router from guaranteeing consistent/correct behavior
- We do not plan to remove the prefix from
Patch Changes
-
Fix
useActionDataso it returns proper contextual action data and not any action data in the tree (#11023) -
Fix bug in
useResolvedPaththat would causeuseResolvedPath(".")in a splat route to lose the splat portion of the URL path. (#10983)- โ ๏ธ This fixes a quite long-standing bug specifically for
"."paths inside a splat route which incorrectly dropped the splat portion of the URL. If you are relative routing via"."inside a splat route in your application you should double check that your logic is not relying on this buggy behavior and update accordingly.
- โ ๏ธ This fixes a quite long-standing bug specifically for
-
Fix issue where a changing fetcher
keyin auseFetcherthat remains mounted wasn't getting picked up (#11009) -
Fix
useFormActionwhich was incorrectly inheriting the?indexquery param from child routeactionsubmissions (#11025) -
Fix
NavLinkactivelogic whentolocation has a trailing slash (#10734) -
Fix types so
unstable_usePromptcan accept aBlockerFunctionin addition to aboolean(#10991) -
Fix
relative="path"bug where relative path calculations started from the full location pathname, instead of from the current contextual route pathname. (#11006)<Route path="/a"> <Route path="/b" element={<Component />}> <Route path="/c" /> </Route> </Route>; function Component() { return ( <> {/* This is now correctly relative to /a/b, not /a/b/c */} <Link to=".." relative="path" /> <Outlet /> </> ); }
Full Changelog: 6.18.0...6.19.0
v6.18.0
Date: 2023-10-31
What's Changed
New Fetcher APIs
Per this RFC, we've introduced some new APIs that give you more granular control over your fetcher behaviors.
- You may now specify your own fetcher identifier via
useFetcher({ key: string }), which allows you to access the same fetcher instance from different components in your application without prop-drilling - Fetcher keys are now exposed on the fetchers returned from
useFetchersso that they can be looked up bykey FormanduseSubmitnow support optionalnavigate/fetcherKeyprops/params to allow kicking off a fetcher submission under the hood with an optionally user-specifiedkey<Form method="post" navigate={false} fetcherKey="my-key">submit(data, { method: "post", navigate: false, fetcherKey: "my-key" })- Invoking a fetcher in this way is ephemeral and stateless
- If you need to access the state of one of these fetchers, you will need to leverage
useFetchers()oruseFetcher({ key })to look it up elsewhere
Persistence Future Flag (future.v7_fetcherPersist)
Per the same RFC as above, we've introduced a new future.v7_fetcherPersist flag that allows you to opt-into the new fetcher persistence/cleanup behavior. Instead of being immediately cleaned up on unmount, fetchers will persist until they return to an idle state. This makes pending/optimistic UI much easier in scenarios where the originating fetcher needs to unmount.
- This is sort of a long-standing bug fix as the
useFetchers()API was always supposed to only reflect in-flight fetcher information for pending/optimistic UI -- it was not intended to reflect fetcher data or hang onto fetchers after they returned to anidlestate - Keep an eye out for the following specific behavioral changes when opting into this flag and check your app for compatibility:
- Fetchers that complete while still mounted will no longer appear in
useFetchers()after completion - they served no purpose in there since you can access the data viauseFetcher().data - Fetchers that previously unmounted while in-flight will not be immediately aborted and will instead be cleaned up once they return to an
idlestate- They will remain exposed via
useFetcherswhile in-flight so you can still access pending/optimistic data after unmount - If a fetcher is no longer mounted when it completes, then it's result will not be post processed - e.g., redirects will not be followed and errors will not bubble up in the UI
- However, if a fetcher was re-mounted elsewhere in the tree using the same
key, then it's result will be processed, even if the originating fetcher was unmounted
- They will remain exposed via
- Fetchers that complete while still mounted will no longer appear in
Minor Changes
- Add fetcher
keyAPIs andnavigate=falseoptions (#10960) - Add
future.v7_fetcherPersistflag (#10962) - Add support for optional path segments in
matchPath(#10768)
Patch Changes
- Fix the
futureprop onBrowserRouter,HashRouterandMemoryRouterso that it accepts aPartial<FutureConfig>instead of requiring all flags to be included (#10962) - Fix
router.getFetcher/router.deleteFetchertype definitions which incorrectly specifiedkeyas an optional parameter (#10960)
Full Changelog: 6.17.0...6.18.0
v6.17.0
Date: 2023-10-16
What's Changed
View Transitions ๐
We're excited to release experimental support for the View Transitions API in React Router! You can now trigger navigational DOM updates to be wrapped in document.startViewTransition to enable CSS animated transitions on SPA navigations in your application.
The simplest approach to enabling a View Transition in your React Router app is via the new <Link unstable_viewTransition> prop. This will cause the navigation DOM update to be wrapped in document.startViewTransition which will enable transitions for the DOM update. Without any additional CSS styles, you'll get a basic cross-fade animation for your page.
If you need to apply more fine-grained styles for your animations, you can leverage the unstable_useViewTransitionState hook which will tell you when a transition is in progress and you can use that to apply classes or styles:
function ImageLink(to, src, alt) {
const isTransitioning = unstable_useViewTransitionState(to);
return (
<Link to={to} unstable_viewTransition>
<img
src={src}
alt={alt}
style={{
viewTransitionName: isTransitioning ? "image-expand" : "",
}}
/>
</Link>
);
}
You can also use the <NavLink unstable_viewTransition> shorthand which will manage the hook usage for you and automatically add a transitioning class to the <a> during the transition:
a.transitioning img {
view-transition-name: "image-expand";
}
<NavLink to={to} unstable_viewTransition>
<img src={src} alt={alt} />
</NavLink>
For an example usage of View Transitions, check out our fork of the awesome Astro Records demo.
For more information on using the View Transitions API, please refer to the Smooth and simple transitions with the View Transitions API guide from the Google Chrome team.
Minor Changes
- Add support for view transitions (#10916)
Patch Changes
- Log a warning and fail gracefully in
ScrollRestorationwhensessionStorageis unavailable (#10848) - Fix
RouterProviderfutureprop type to be aPartial<FutureConfig>so that not all flags must be specified (#10900) - Allow 404 detection to leverage root route error boundary if path contains a URL segment (#10852)
- Fix
ErrorResponsetype to avoid leaking internal field (#10876)
Full Changelog: 6.16.0...6.17.0
v6.16.0
Date: 2023-09-13
Minor Changes
- In order to move towards stricter TypeScript support in the future, we're aiming to replace current usages of
anywithunknownon exposed typings for user-provided data. To do this in Remix v2 without introducing breaking changes in React Router v6, we have added generics to a number of shared types. These continue to default toanyin React Router and are overridden withunknownin Remix. In React Router v7 we plan to move these tounknownas a breaking change. (#10843)Locationnow accepts a generic for thelocation.statevalueActionFunctionArgs/ActionFunction/LoaderFunctionArgs/LoaderFunctionnow accept a generic for thecontextparameter (only used in SSR usages viacreateStaticHandler)- The return type of
useMatches(now exported asUIMatch) accepts generics formatch.dataandmatch.handle- both of which were already set tounknown
- Move the
@privateclass exportErrorResponseto anUNSAFE_ErrorResponseImplexport since it is an implementation detail and there should be no construction ofErrorResponseinstances in userland. This frees us up to export atype ErrorResponsewhich correlates to an instance of the class viaInstanceType. Userland code should only ever be usingErrorResponseas a type and should be type-narrowing viaisRouteErrorResponse. (#10811) - Export
ShouldRevalidateFunctionArgsinterface (#10797) - Removed private/internal APIs only required for the Remix v1 backwards compatibility layer and no longer needed in Remix v2 (
_isFetchActionRedirect,_hasFetcherDoneAnything) (#10715)
Patch Changes
- Properly encode rendered URIs in server rendering to avoid hydration errors (#10769)
- Add method/url to error message on aborted
query/queryRoutecalls (#10793) - Fix a race-condition with loader/action-thrown errors on
route.lazyroutes (#10778) - Fix type for
actionResulton the arguments object passed toshouldRevalidate(#10779)
Full Changelog: v6.15.0...v6.16.0
v6.15.0
Date: 2023-08-10
Minor Changes
- Add's a new
redirectDocument()function which allows users to specify that a redirect from aloader/actionshould trigger a document reload (viawindow.location) instead of attempting to navigate to the redirected location via React Router (#10705)
Patch Changes
- Ensure
useRevalidatoris referentially stable across re-renders if revalidations are not actively occurring (#10707) - Ensure hash history always includes a leading slash on hash pathnames (#10753)
- Fixes an edge-case affecting web extensions in Firefox that use
URLSearchParamsand theuseSearchParamshook (#10620) - Reorder effects in
unstable_usePromptto avoid throwing an exception if the prompt is unblocked and a navigation is performed synchronously (#10687, #10718) - SSR: Do not include hash in
useFormAction()for unspecified actions since it cannot be determined on the server and causes hydration issues (#10758) - SSR: Fix an issue in
queryRoutethat was not always identifying thrownResponseinstances (#10717) react-router-native: Update@ungap/url-search-paramsdependency from^0.1.4to^0.2.2(#10590)
Full Changelog: v6.14.2...v6.15.0
v6.14.2
Date: 2023-07-17
Patch Changes
- Add missing
<Form state>prop to populatehistory.stateon submission navigations (#10630) - Trigger an error if a
deferpromise resolves/rejects withundefinedin order to match the behavior of loaders and actions which must return a value ornull(#10690) - Properly handle fetcher redirects interrupted by normal navigations (#10674)
- Initial-load fetchers should not automatically revalidate on GET navigations (#10688)
- Properly decode element id when emulating hash scrolling via
<ScrollRestoration>(#10682) - Typescript: Enhance the return type of
Route.lazyto prohibit returning an empty object (#10634) - SSR: Support proper hydration of
Errorsubclasses such asReferenceError/TypeError(#10633)
Full Changelog: v6.14.1...v6.14.2
v6.14.1
Date: 2023-06-30
Patch Changes
- Fix loop in
unstable_useBlockerwhen used with an unstable blocker function (#10652) - Fix issues with reused blockers on subsequent navigations (#10656)
- Updated dependencies:
@remix-run/router@1.7.1
Full Changelog: v6.14.0...v6.14.1
v6.14.0
Date: 2023-06-23
What's Changed
JSON/Text Submissions
6.14.0 adds support for JSON and Text submissions via useSubmit/fetcher.submit since it's not always convenient to have to serialize into FormData if you're working in a client-side SPA. To opt-into these encodings you just need to specify the proper formEncType:
Opt-into application/json encoding:
function Component() {
let navigation = useNavigation();
let submit = useSubmit();
submit({ key: "value" }, { method: "post", encType: "application/json" });
// navigation.formEncType => "application/json"
// navigation.json => { key: "value" }
}
async function action({ request }) {
// request.headers.get("Content-Type") => "application/json"
// await request.json() => { key: "value" }
}
Opt-into text/plain encoding:
function Component() {
let navigation = useNavigation();
let submit = useSubmit();
submit("Text submission", { method: "post", encType: "text/plain" });
// navigation.formEncType => "text/plain"
// navigation.text => "Text submission"
}
async function action({ request }) {
// request.headers.get("Content-Type") => "text/plain"
// await request.text() => "Text submission"
}
โ ๏ธ Default Behavior Will Change in v7
Please note that to avoid a breaking change, the default behavior will still encode a simple key/value JSON object into a FormData instance:
function Component() {
let navigation = useNavigation();
let submit = useSubmit();
submit({ key: "value" }, { method: "post" });
// navigation.formEncType => "application/x-www-form-urlencoded"
// navigation.formData => FormData instance
}
async function action({ request }) {
// request.headers.get("Content-Type") => "application/x-www-form-urlencoded"
// await request.formData() => FormData instance
}
This behavior will likely change in v7 so it's best to make any JSON object submissions explicit with formEncType: "application/x-www-form-urlencoded" or formEncType: "application/json" to ease your eventual v7 migration path.
Minor Changes
- Add support for
application/jsonandtext/plainencodings foruseSubmit/fetcher.submit. To reflect these additional types,useNavigation/useFetchernow also containnavigation.json/navigation.textandfetcher.json/fetcher.textwhich include the json/text submission if applicable. (#10413)
Patch Changes
- When submitting a form from a
submitterelement, prefer the built-innew FormData(form, submitter)instead of the previous manual approach in modern browsers (those that support the newsubmitterparameter) (#9865)- For browsers that don't support it, we continue to just append the submit button's entry to the end, and we also add rudimentary support for
type="image"buttons - If developers want full spec-compliant support for legacy browsers, they can use the
formdata-submitter-polyfill
- For browsers that don't support it, we continue to just append the submit button's entry to the end, and we also add rudimentary support for
- Call
window.history.pushState/replaceStatebefore updating React Router state (instead of after) so thatwindow.locationmatchesuseLocationduring synchronous React 17 rendering (#10448)- โ ๏ธ Note: generally apps should not be relying on
window.locationand should always referenceuseLocationwhen possible, aswindow.locationwill not be in sync 100% of the time (due topopstateevents, concurrent mode, etc.)
- โ ๏ธ Note: generally apps should not be relying on
- Avoid calling
shouldRevalidatefor fetchers that have not yet completed a data load (#10623) - Strip
basenamefrom thelocationprovided to<ScrollRestoration getKey>to match theuseLocationbehavior (#10550) - Strip
basenamefrom locations provided tounstable_useBlockerfunctions to match theuseLocationbehavior (#10573) - Fix
unstable_useBlockerkey issues inStrictMode(#10573) - Fix
generatePathwhen passed a numeric0value parameter (#10612) - Fix
tsc --skipLibCheck:falseissues on React 17 (#10622) - Upgrade
typescriptto 5.1 (#10581)
Full Changelog: v6.13.0...v6.14.0
v6.13.0
Date: 2023-06-14
What's Changed
6.13.0 is really a patch release in spirit but comes with a SemVer minor bump since we added a new future flag.
future.v7_startTransition
The tl;dr; is that 6.13.0 is the same as 6.12.0 bue we've moved the usage of React.startTransition behind an opt-in future.v7_startTransition future flag because we found that there are applications in the wild that are currently using Suspense in ways that are incompatible with React.startTransition.
Therefore, in 6.13.0 the default behavior will no longer leverage React.startTransition:
<BrowserRouter>
<Routes>{/*...*/}</Routes>
</BrowserRouter>
<RouterProvider router={router} />
If you wish to enable React.startTransition, pass the future flag to your router component:
<BrowserRouter future={{ v7_startTransition: true }}>
<Routes>{/*...*/}</Routes>
</BrowserRouter>
<RouterProvider router={router} future={{ v7_startTransition: true }}/>
We recommend folks adopt this flag sooner rather than later to be better compatible with React concurrent mode, but if you run into issues you can continue without the use of React.startTransition until v7. Issues usually boil down to creating net-new promises during the render cycle, so if you run into issues when opting into React.startTransition, you should either lift your promise creation out of the render cycle or put it behind a useMemo.
Minor Changes
- Move
React.startTransitionusage behinds a future flag (#10596)
Patch Changes
- Work around webpack/terser
React.startTransitionminification bug in production mode (#10588)
Full Changelog: v6.12.1...v6.13.0
v6.12.1
Date: 2023-06-08
Warning
Please use version 6.13.0 or later instead of 6.12.0/6.12.1. These versions suffered from some Webpack build/minification issues resulting failed builds or invalid minified code in your production bundles. See #10569 and #10579 for more details.
Patch Changes
- Adjust feature detection of
React.startTransitionto fix webpack + react 17 compilation error (#10569)
Full Changelog: v6.12.0...v6.12.1
v6.12.0
Date: 2023-06-06
Warning
Please use version 6.13.0 or later instead of 6.12.0/6.12.1. These versions suffered from some Webpack build/minification issues resulting failed builds or invalid minified code in your production bundles. See #10569 and #10579 for more details.
What's Changed
React.startTransition support
With 6.12.0 we've added better support for suspending components by wrapping the internal router state updates in React.startTransition. This means that, for example, if one of your components in a destination route suspends and you have not provided a Suspense boundary to show a fallback, React will delay the rendering of the new UI and show the old UI until that asynchronous operation resolves. This could be useful for waiting for things such as waiting for images or CSS files to load (and technically, yes, you could use it for data loading but we'd still recommend using loaders for that ๐). For a quick overview of this usage, check out Ryan's demo on Twitter.
Minor Changes
- Wrap internal router state updates with
React.startTransition(#10438)
Patch Changes
- Allow fetcher revalidations to complete if submitting fetcher is deleted (#10535)
- Re-throw
DOMException(DataCloneError) when attempting to perform aPUSHnavigation with non-serializable state. (#10427) - Ensure revalidations happen when hash is present (#10516)
- Upgrade
jestandjsdom(#10453) - Updated dependencies:
@remix-run/router@1.6.3(Changelog)
Full Changelog: v6.11.2...v6.12.0
v6.11.2
Date: 2023-05-17
Patch Changes
- Fix
basenameduplication in descendant<Routes>inside a<RouterProvider>(#10492) - Fix bug where initial data load would not kick off when hash is present (#10493)
- Export
SetURLSearchParamstype (#10444) - Fix Remix HMR-driven error boundaries by properly reconstructing new routes and
manifestin_internalSetRoutes(#10437)
Full Changelog: v6.11.1...v6.11.2
v6.11.1
Date: 2023-05-03
Patch Changes
- Fix usage of
ComponentAPI within descendant<Routes>(#10434) - Fix bug when calling
useNavigatefrom<Routes>inside a<RouterProvider>(#10432) - Fix usage of
<Navigate>in strict mode when using a data router (#10435) - Fix
basenamehandling when navigating without a path (#10433) - "Same hash" navigations no longer re-run loaders to match browser behavior (i.e.
/path#hash -> /path#hash) (#10408)
Full Changelog: v6.11.0...v6.11.1
v6.11.0
Date: 2023-04-28
Minor Changes
- Enable
basenamesupport inuseFetcher(#10336)- If you were previously working around this issue by manually prepending the
basenamethen you will need to remove the manually prependedbasenamefrom yourfetchercalls (fetcher.load('/basename/route') -> fetcher.load('/route'))
- If you were previously working around this issue by manually prepending the
- Updated dependencies:
@remix-run/router@1.6.0(Changelog)
Patch Changes
- When using a
RouterProvider,useNavigate/useSubmit/fetcher.submitare now stable across location changes, since we can handle relative routing via the@remix-run/routerinstance and get rid of our dependence onuseLocation()(#10336)- When using
BrowserRouter, these hooks remain unstable across location changes because they still rely onuseLocation()
- When using
- Fetchers should no longer revalidate on search params changes or routing to the same URL, and will only revalidate on
actionsubmissions orrouter.revalidatecalls (#10344) - Fix inadvertent re-renders when using
Componentinstead ofelementon a route definition (#10287) - Fail gracefully on
<Link to="//">and other invalid URL values (#10367) - Switched from
useSyncExternalStoretouseStatefor internal@remix-run/routerrouter state syncing in<RouterProvider>. We found some subtle bugs where router state updates got propagated before other normaluseStateupdates, which could lead to foot guns inuseEffectcalls. (#10377, #10409) - Log loader/action errors caught by the default error boundary to the console in dev for easier stack trace evaluation (#10286)
- Fix bug preventing rendering of descendant
<Routes>whenRouterProvidererrors existed (#10374) - Fix detection of
useNavigatein the render cycle by setting theactiveRefin a layout effect, allowing thenavigatefunction to be passed to child components and called in auseEffectthere (#10394) - Allow
useRevalidator()to resolve a loader-driven error boundary scenario (#10369) - Enhance
LoaderFunction/ActionFunctionreturn type to preventundefinedfrom being a valid return value (#10267) - Ensure proper 404 error on
fetcher.loadcall to a route without aloader(#10345) - Decouple
AbortControllerusage between revalidating fetchers and the thing that triggered them such that the unmount/deletion of a revalidating fetcher doesn't impact the ongoing triggering navigation/revalidation (#10271)
Full Changelog: v6.10.0...v6.11.0
v6.10.0
Date: 2023-03-29
What's Changed
We recently published a post over on the Remix Blog titled "Future Proofing Your Remix App" that goes through our strategy to ensure smooth upgrades for your Remix and React Router apps going forward. React Router 6.10.0 adds support for these flags (for data routers) which you can specify when you create your router:
const router = createBrowserRouter(routes, {
future: {
// specify future flags here
},
});
You can also check out the docs here and here.
Minor Changes
future.v7_normalizeFormMethod
The first future flag being introduced is future.v7_normalizeFormMethod which will normalize the exposed useNavigation()/useFetcher() formMethod fields as uppercase HTTP methods to align with the fetch() (and some Remix) behavior. (#10207)
- When
future.v7_normalizeFormMethodis unspecified or set tofalse(default v6 behavior),useNavigation().formMethodis lowercaseuseFetcher().formMethodis lowercase
- When
future.v7_normalizeFormMethod === true:useNavigation().formMethodis UPPERCASEuseFetcher().formMethodis UPPERCASE
Patch Changes
- Fix
createStaticHandlerto also check forErrorBoundaryon routes in addition toerrorElement(#10190) - Fix route ID generation when using Fragments in
createRoutesFromElements(#10193) - Provide fetcher submission to
shouldRevalidateif the fetcher action redirects (#10208) - Properly handle
lazy()errors during router initialization (#10201) - Remove
instanceofcheck forDeferredDatato be resilient to ESM/CJS boundaries in SSR bundling scenarios (#10247) - Update to latest
@remix-run/web-fetch@4.3.3(#10216)
Full Changelog: v6.9.0...v6.10.0
v6.9.0
Date: 2023-03-10
What's Changed
Component/ErrorBoundary route properties
React Router now supports an alternative way to define your route element and errorElement fields as React Components instead of React Elements. You can instead pass a React Component to the new Component and ErrorBoundary fields if you choose. There is no functional difference between the two, so use whichever approach you prefer ๐. You shouldn't be defining both, but if you do Component/ErrorBoundary will "win"
Example JSON Syntax
// Both of these work the same:
const elementRoutes = [{
path: '/',
element: <Home />,
errorElement: <HomeError />,
}]
const componentRoutes = [{
path: '/',
Component: Home,
ErrorBoundary: HomeError,
}]
function Home() { ... }
function HomeError() { ... }
Example JSX Syntax
// Both of these work the same:
const elementRoutes = createRoutesFromElements(
<Route path='/' element={<Home />} errorElement={<HomeError /> } />
);
const componentRoutes = createRoutesFromElements(
<Route path='/' Component={Home} ErrorBoundary={HomeError} />
);
function Home() { ... }
function HomeError() { ... }
Introducing Lazy Route Modules
In order to keep your application bundles small and support code-splitting of your routes, we've introduced a new lazy() route property. This is an async function that resolves the non-route-matching portions of your route definition (loader, action, element/Component, errorElement/ErrorBoundary, shouldRevalidate, handle).
Lazy routes are resolved on initial load and during the loading or submitting phase of a navigation or fetcher call. You cannot lazily define route-matching properties (path, index, children) since we only execute your lazy route functions after we've matched known routes.
Your lazy functions will typically return the result of a dynamic import.
// In this example, we assume most folks land on the homepage so we include that
// in our critical-path bundle, but then we lazily load modules for /a and /b so
// they don't load until the user navigates to those routes
let routes = createRoutesFromElements(
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="a" lazy={() => import("./a")} />
<Route path="b" lazy={() => import("./b")} />
</Route>,
);
Then in your lazy route modules, export the properties you want defined for the route:
export async function loader({ request }) {
let data = await fetchData(request);
return json(data);
}
// Export a `Component` directly instead of needing to create a React Element from it
export function Component() {
let data = useLoaderData();
return (
<>
<h1>You made it!</h1>
<p>{data}</p>
</>
);
}
// Export an `ErrorBoundary` directly instead of needing to create a React Element from it
export function ErrorBoundary() {
let error = useRouteError();
return isRouteErrorResponse(error) ? (
<h1>
{error.status} {error.statusText}
</h1>
) : (
<h1>{error.message || error}</h1>
);
}
An example of this in action can be found in the examples/lazy-loading-router-provider directory of the repository. For more info, check out the lazy docs.
๐ Huge thanks to @rossipedia for the Initial Proposal and POC Implementation.
Minor Changes
- Add support for
route.Component/route.ErrorBoundaryproperties (#10045) - Add support for
route.lazy(#10045)
Patch Changes
- Improve memoization for context providers to avoid unnecessary re-renders (#9983)
- Fix
generatePathincorrectly applying parameters in some cases (#10078) [react-router-dom-v5-compat]Add missed data router API re-exports (#10171)
Full Changelog: v6.8.2...v6.9.0
v6.8.2
Date: 2023-02-27
Patch Changes
- Treat same-origin absolute URLs in
<Link to>as external if they are outside of the routerbasename(#10135) - Correctly perform a hard redirect for same-origin absolute URLs outside of the router
basename(#10076) - Fix SSR of absolute
<Link to>urls (#10112) - Properly escape HTML characters in
StaticRouterProviderserialized hydration data (#10068) - Fix
useBlockerto returnIDLE_BLOCKERduring SSR (#10046) - Ensure status code and headers are maintained for
deferloader responses increateStaticHandler'squery()method (#10077) - Change
invariantto anUNSAFE_invariantexport since it's only intended for internal use (#10066)
Full Changelog: v6.8.1...v6.8.2
v6.8.1
Date: 2023-02-06
Patch Changes
- Remove inaccurate console warning for POP navigations and update active blocker logic (#10030)
- Only check for differing origin on absolute URL redirects (#10033)
- Improved absolute url detection in
Linkcomponent (now also supportsmailto:urls) (#9994) - Fix partial object (search or hash only) pathnames losing current path value (#10029)
Full Changelog: v6.8.0...v6.8.1
v6.8.0
Date: 2023-01-26
Minor Changes
Support absolute URLs in <Link to>. If the URL is for the current origin, it will still do a client-side navigation. If the URL is for a different origin then it will do a fresh document request for the new origin. (#9900)
<Link to="https://neworigin.com/some/path"> {/* Document request */}
<Link to="//neworigin.com/some/path"> {/* Document request */}
<Link to="https://www.currentorigin.com/path"> {/* Client-side navigation */}
Patch Changes
- Fixes 2 separate issues for revalidating fetcher
shouldRevalidatecalls (#9948)- The
shouldRevalidatefunction was only being called for explicit revalidation scenarios (after a mutation, manualuseRevalidatorcall, or anX-Remix-Revalidateheader used for cookie setting in Remix). It was not properly being called on implicit revalidation scenarios that also apply to navigationloaderrevalidation, such as a change in search params or clicking a link for the page we're already on. It's now correctly called in those additional scenarios. - The parameters being passed were incorrect and inconsistent with one another since the
current*/next*parameters reflected the staticfetcher.loadURL (and thus were identical). Instead, they should have reflected the navigation that triggered the revalidation (as theform*parameters did). These parameters now correctly reflect the triggering navigation.
- The
- Fix bug with search params removal via
useSearchParams(#9969) - Respect
preventScrollReseton<fetcher.Form>(#9963) - Fix navigation for hash routers on manual URL changes (#9980)
- Use
pagehideinstead ofbeforeunloadfor<ScrollRestoration>. This has better cross-browser support, specifically on Mobile Safari. (#9945) - Do not short circuit on hash change only mutation submissions (#9944)
- Remove
instanceofcheck fromisRouteErrorResponseto avoid bundling issues on the server (#9930) - Detect when a
defercall only contains critical data and remove theAbortController(#9965) - Send the name as the value when url-encoding
FileFormDataentries (#9867) react-router-dom-v5-compat- Fix SSRuseLayoutEffectconsole.errorwhen usingCompatRouter(#9820)
Full Changelog: v6.7.0...v6.8.0
v6.7.0
Date: 2023-01-18
Minor Changes
- Add
unstable_useBlocker/unstable_usePrompthooks for blocking navigations within the app's location origin (#9709, #9932) - Add
preventScrollResetprop to<Form>(#9886)
Patch Changes
- Added pass-through event listener options argument to
useBeforeUnload(#9709) - Fix
generatePathwhen optional params are present (#9764) - Update
<Await>to acceptReactNodeas children function return result (#9896) - Improved absolute redirect url detection in actions/loaders (#9829)
- Fix URL creation with memory histories (#9814)
- Fix scroll reset if a submission redirects (#9886)
- Fix 404 bug with same-origin absolute redirects (#9913)
- Streamline
jsdombug workaround in tests (#9824)
Full Changelog: v6.6.2...v6.7.0
v6.6.2
Date: 2023-01-09
Patch Changes
- Ensure
useIdconsistency during SSR (#9805)
Full Changelog: v6.6.1...v6.6.2
v6.6.1
Date: 2022-12-23
Patch Changes
- Include submission info in
shouldRevalidateon action redirects (#9777, #9782) - Reset
actionDataon action redirect to current location (#9772)
Full Changelog: v6.6.0...v6.6.1
v6.6.0
Date: 2022-12-21
What's Changed
This minor release is primarily to stabilize our SSR APIs for Data Routers now that we've wired up the new RouterProvider in Remix as part of the React Router-ing Remix work.
Minor Changes
- Remove
unstable_prefix fromcreateStaticHandler/createStaticRouter/StaticRouterProvider(#9738) - Add
useBeforeUnload()hook (#9664)
Patch Changes
- Support uppercase
<Form method>anduseSubmitmethod values (#9664) - Fix
<button formmethod>form submission overriddes (#9664) - Fix explicit
replaceon submissions andPUSHon submission to new paths (#9734) - Prevent
useLoaderDatausage inerrorElement(#9735) - Proper hydration of
Errorobjects fromStaticRouterProvider(#9664) - Skip initial scroll restoration for SSR apps with
hydrationData(#9664) - Fix a few bugs where loader/action data wasn't properly cleared on errors (#9735)
Full Changelog: v6.5.0...v6.6.0
v6.5.0
Date: 2022-12-16
What's Changed
This release introduces support for Optional Route Segments. Now, adding a ? to the end of any path segment will make that entire segment optional. This works for both static segments and dynamic parameters.
Optional Params Examples
<Route path=":lang?/about>will match:/:lang/about/about
<Route path="/multistep/:widget1?/widget2?/widget3?">will match:/multistep/multistep/:widget1/multistep/:widget1/:widget2/multistep/:widget1/:widget2/:widget3
Optional Static Segment Example
<Route path="/home?">will match://home
<Route path="/fr?/about">will match:/about/fr/about
Minor Changes
- Allows optional routes and optional static segments (#9650)
Patch Changes
- Stop incorrectly matching on partial named parameters, i.e.
<Route path="prefix-:param">, to align with how splat parameters work. If you were previously relying on this behavior then it's recommended to extract the static portion of the path at theuseParamscall site: (#9506)
// Old behavior at URL /prefix-123
<Route path="prefix-:id" element={<Comp /> }>
function Comp() {
let params = useParams(); // { id: '123' }
let id = params.id; // "123"
...
}
// New behavior at URL /prefix-123
<Route path=":id" element={<Comp /> }>
function Comp() {
let params = useParams(); // { id: 'prefix-123' }
let id = params.id.replace(/^prefix-/, ''); // "123"
...
}
- Persist
headersonloaderrequest's after SSR documentactionrequest (#9721) - Fix requests sent to revalidating loaders so they reflect a GET request (#9660)
- Fix issue with deeply nested optional segments (#9727)
- GET forms now expose a submission on the loading navigation (#9695)
- Fix error boundary tracking for multiple errors bubbling to the same boundary (#9702)
Full Changelog: v6.4.5...v6.5.0
v6.4.5
Date: 2022-12-07
Patch Changes
- Fix requests sent to revalidating loaders so they reflect a
GETrequest (#9680) - Remove
instanceof Responsechecks in favor ofisResponse(#9690) - Fix
URLcreation in Cloudflare Pages or other non-browser-environments (#9682, #9689) - Add
requestContextsupport to static handlerquery/queryRoute(#9696)- Note that the unstable API of
queryRoute(path, routeId)has been changed toqueryRoute(path, { routeId, requestContext })
- Note that the unstable API of
Full Changelog: v6.4.4...v6.4.5
v6.4.4
Date: 2022-11-30
Patch Changes
- Throw an error if an
action/loaderfunction returnsundefinedas revalidations need to know whether the loader has previously been executed.undefinedalso causes issues during SSR stringification for hydration. You should always ensure yourloader/actionreturns a value, and you may returnnullif you don't wish to return anything. (#9511) - Properly handle redirects to external domains (#9590, #9654)
- Preserve the HTTP method on 307/308 redirects (#9597)
- Support
basenamein static data routers (#9591) - Enhanced
ErrorResponsebodies to contain more descriptive text in internal 403/404/405 scenarios - Fix issues with encoded characters in
NavLinkand descendant<Routes>(#9589, #9647) - Properly serialize/deserialize
ErrorResponseinstances when using built-in hydration (#9593) - Support
basenamein static data routers (#9591) - Updated dependencies:
@remix-run/router@1.0.4react-router@6.4.4
Full Changelog: v6.4.3...v6.4.4
v6.4.3
Date: 2022-11-01
Patch Changes
- Generate correct
<a href>values when usingcreateHashRouter(#9409) - Better handle encoding/matching with special characters in URLs and route paths (#9477, #9496)
- Generate correct
formActionpathnames when anindexroute also has apath(#9486) - Respect
relative=pathprop onNavLink(#9453) - Fix
NavLinkbehavior for root urls (#9497) useRoutesshould be able to returnnullwhen passinglocationArg(#9485)- Fix
initialEntriestype increateMemoryRouter(#9498) - Support
basenameand relative routing inloader/actionredirects (#9447) - Ignore pathless layout routes when looking for proper submission
actionfunction (#9455) - Add UMD build for
@remix-run/router(#9446) - Fix
createURLin local file execution in Firefox (#9464)
Full Changelog: v6.4.2...v6.4.3
v6.4.2
Date: 2022-10-06
Patch Changes
- Respect
basenameinuseFormAction(#9352) - Fix
IndexRouteObjectandNonIndexRouteObjecttypes to makehasErrorElementoptional (#9394) - Enhance console error messages for invalid usage of data router hooks (#9311)
- If an index route has children, it will result in a runtime error. We have strengthened our
RouteObject/RoutePropstypes to surface the error in TypeScript. (#9366)
Full Changelog: v6.4.1...v6.4.2
v6.4.1
Date: 2022-09-22
Patch Changes
- Preserve state from
initialEntries(#9288) - Preserve
?indexfor fetcher get submissions to index routes (#9312)
Full Changelog: v6.4.0...v6.4.1
v6.4.0
Date: 2022-09-13
What's Changed
Remix Data APIs
Whoa this is a big one! 6.4.0 brings all the data loading and mutation APIs over from Remix. Here's a quick high level overview, but it's recommended you go check out the docs, especially the feature overview and the tutorial.
New react-router APIs
- Create your router with
createMemoryRouter - Render your router with
<RouterProvider> - Load data with a Route
loaderand mutate with a Routeaction - Handle errors with Route
errorElement - Defer non-critical data with
deferandAwait
New react-router-dom APIs
- Create your router with
createBrowserRouter/createHashRouter - Submit data with the new
<Form>component - Perform in-page data loads and mutations with
useFetcher() - Defer non-critical data with
deferandAwait - Manage scroll position with
<ScrollRestoration> - Perform path-relative navigations with
<Link relative="path">(#9160)
Patch Changes
- Path resolution is now trailing slash agnostic (#8861)
useLocationreturns the scoped location inside a<Routes location>component (#9094)- Respect the
<Link replace>prop if it is defined (#8779)
Full Changelog: v6.3.0...v6.4.0
v6.3.0
Date: 2022-03-31
Minor Changes
- Added the v5 to v6 backwards compatibility package ๐ (#8752). The official guide can be found in this discussion
Full Changelog: v6.2.2...v6.3.0
v6.2.2
Date: 2022-02-28
Patch Changes
- Fixed nested splat routes that begin with special URL-safe characters (#8563)
- Fixed a bug where index routes were missing route context in some cases (#8497)
Full Changelog: v6.2.1...v6.2.2
v6.2.1
Date: 2021-12-17
Patch Changes
- This release updates the internal
historydependency to5.2.0.
Full Changelog: v6.2.0...v6.2.1
v6.2.0
Date: 2021-12-17
Minor Changes
- We now use statically analyzable CJS exports. This enables named imports in Node ESM scripts (See the commit).
Patch Changes
- Fixed the
RoutePropselementtype, which should be aReactNode(#8473) - Fixed a bug with
useOutletfor top-level routes (#8483)
Full Changelog: v6.1.1...v6.2.0
v6.1.1
Date: 2021-12-11
Patch Changes
- In v6.1.0 we inadvertently shipped a new, undocumented API that will likely introduce bugs (#7586). We have flagged
HistoryRouterasunstable_HistoryRouter, as this API will likely need to change before a new major release.
Full Changelog: v6.1.0...v6.1.1
v6.1.0
Date: 2021-12-10
Minor Changes
<Outlet>can now receive acontextprop. This value is passed to child routes and is accessible via the newuseOutletContexthook. See the API docs for details. (#8461)<NavLink>can now receive a child function for access to its props. (#8164)- Improved TypeScript signature for
useMatchandmatchPath. For example, when you calluseMatch("foo/:bar/:baz"), the path is parsed and the return type will bePathMatch<"bar" | "baz">. (#8030)
Patch Changes
- Fixed a bug that broke support for base64 encoded IDs on nested routes (#8291)
- A few error message improvements (#8202)
Full Changelog: v6.0.2...v6.1.0
v6.0.2
Date: 2021-11-09
Patch Changes
- Added the
reloadDocumentprop to<Link>. This allows<Link>to function like a normal anchor tag by reloading the document after navigation while maintaining the relativetoresolution (#8283)
Full Changelog: v6.0.1...v6.0.2
v6.0.1
Date: 2021-11-05
Patch Changes
- Add a default
<StaticRouter location>value (#8243) - Add invariant for using
<Route>inside<Routes>to help people make the change (#8238)
Full Changelog: v6.0.0...v6.0.1
v6.0.0
Date: 2021-11-03
React Router v6 is here!
Please go read our blog post for more information on all the great stuff in v6 including notes about how to upgrade from React Router v5 and Reach Router.