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
- Full Changelog:
v6.28.0...v7.0.0
- <<<<<<< HEAD
- 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
Date: 2024-11-21
- The
react-router-dom
,@remix-run/react
,@remix-run/server-runtime
, and@remix-run/router
have been collapsed into thereact-router
package- To ease migration,
react-router-dom
is still published in v7 as a re-export of everything fromreact-router
- To ease migration,
- The
@remix-run/cloudflare-pages
and@remix-run/cloudflare-workers
have been collapsed into@react-router/cloudflare
package` - The
react-router-dom-v5-compat
andreact-router-native
packages are removed starting with v7
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";
The following APIs have been removed in React Router v7:
json
defer
unstable_composeUploadHandlers
unstable_createMemoryUploadHandler
unstable_parseMultipartFormData
React Router v7 requires the following minimum versions:
node@20
- React Router no longer provides an
installGlobals
method to polyfill thefetch
API
- React Router no longer provides an
react@18
,react-dom@18
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 introduce din 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_relativeSplatPath
future.v7_startTransition
future.v7_fetcherPersist
future.v7_normalizeFormMethod
future.v7_partialHydration
future.v7_skipActionStatusRevalidation
- Remix v2 flags
future.v3_fetcherPersist
future.v3_relativeSplatPath
future.v3_throwAbortReason
future.v3_singleFetch
future.v3_lazyRouteDiscovery
future.v3_optimizeDeps
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 Vite Plugin 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:
import { reactRouter } from "@react-router/dev/vite";
import { writeFile } from "node:fs/promises";
export default {
plugins: [
reactRouter({
async buildEnd({ buildManifest }) {
await writeFile(
"build/manifest.json",
JSON.stringify(buildManifest, null, 2),
"utf-8"
);
},
}),
],
};
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().load
useFetcher().submit
useRevalidator().revalidate()
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"),
+ ]),
+];
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>
.
For example:
// app/routes/product.tsx
import type * as Route from "./+types.product";
export function loader({ params }: Route.LoaderArgs) {}
export default function Component({ loaderData }: Route.ComponentProps) {}
This initial implementation targets type inference for:
Params
: Path parameters from your routing config inroutes.ts
including file-based routingLoaderData
: Loader data fromloader
and/orclientLoader
within your route moduleActionData
: Action data fromaction
and/orclientAction
within your route module
These types are then used to create types for route export args and props:
LoaderArgs
ClientLoaderArgs
ActionArgs
ClientActionArgs
HydrateFallbackProps
ComponentProps
(for thedefault
export)ErrorBoundaryProps
In the future, we plan to add types for the rest of the route module exports: meta
, links
, headers
, shouldRevalidate
, etc.
We also plan to generate types for typesafe Link
s:
<Link to="/products/:id" params={{ id: 1 }} />
// ^^^^^^^^^^^^^ ^^^^^^^^^
// typesafe `to` and `params` based on the available routes in your app
React Router will generate types into a .react-router/
directory at the root of your app. This directory is fully managed by React Router and is derived based on your route config (routes.ts
).
👉 Add .react-router/
to .gitignore
.react-router
You should also ensure that generated types for routes are always present before running typechecking, especially for running typechecking in CI.
👉 Add react-router typegen
to your typecheck
command in package.json
{
"scripts": {
"typecheck": "react-router typegen && tsc"
}
}
To get TypeScript to use those generated types, you'll need to add them to include
in tsconfig.json
. And to be able to import them as if they files next to your route modules, you'll also need to configure rootDirs
.
👉 Configure tsconfig.json
for generated types
{
"include": [".react-router/types/**/*"],
"compilerOptions": {
"rootDirs": [".", "./.react-router/types"]
}
}
You can manually generate types with the new typegen
command:
react-router typegen
However, manual type generation is tedious and types can get out of sync quickly if you ever forget to run typegen
. Instead, we recommend that you setup our new TypeScript plugin which will automatically generate fresh types whenever routes change. That way, you'll always have up-to-date types.
To get automatic type generation, you can use our new TypeScript plugin.
👉 Add the TypeScript plugin to tsconfig.json
{
"compilerOptions": {
"plugins": [{ "name": "@react-router/dev" }]
}
}
We plan to add some other goodies to our TypeScript plugin soon, including:
- Automatic
jsdoc
for route exports that include links to official docs - Autocomplete for route exports
- Warnings for non-HMR compliant exports
TypeScript looks for plugins registered in tsconfig.json
in the local node_modules/
,
but VSCode ships with its own copy of TypeScript that is installed outside of your project.
For TypeScript plugins to work, you'll need to tell VSCode to use the local workspace version of TypeScript.
👉 Ensure that VSCode is using the workspace version of TypeScript
This should already be set up for you by a .vscode/settings.json
:
{
"typescript.tsdk": "node_modules/typescript/lib"
}
Alternatively, you can open up any TypeScript file and use CMD+SHIFT+P to find Select TypeScript Version
and then select Use Workspace Version
. You may need to quit VSCode and reopen it for this setting to take effect.
In VSCode, open up any TypeScript file in your project and then use CMD+SHIFT+P to select Open TS Server log
. There should be a log for [react-router] setup
that indicates that the plugin was resolved correctly. Then look for any errors in the log.
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"];
}
- Remove the original
defer
implementation in favor of using raw promises via single fetch andturbo-stream
(#11744)- This removes these exports from React Router:
defer
AbortedDeferredError
type TypedDeferredData
UNSAFE_DeferredData
UNSAFE_DEFERRED_SYMBOL
- This removes these exports from React Router:
- Collapse packages into
react-router
(#11505)@remix-run/router
react-router-dom
@remix-run/server-runtime
@remix-run/testing
- As a note, the
react-router-dom
package 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_startTransition
flag (#11696) - Expose the underlying router promises from the following APIs for composition in React 19 APIs: (#11521)
- Remove
future.v7_normalizeFormMethod
future flag (#11697) - Imports/Exports cleanup (#11840)
- Removed the following exports that were previously public API from
@remix-run/router
- types
AgnosticDataIndexRouteObject
AgnosticDataNonIndexRouteObject
AgnosticDataRouteMatch
AgnosticDataRouteObject
AgnosticIndexRouteObject
AgnosticNonIndexRouteObject
AgnosticRouteMatch
AgnosticRouteObject
TrackedPromise
unstable_AgnosticPatchRoutesOnMissFunction
Action
-> exported asNavigationType
viareact-router
Router
exported asRemixRouter
to differentiate from RR's<Router>
- API
getToPathname
(@private
)joinPaths
(@private
)normalizePathname
(@private
)resolveTo
(@private
)stripBasename
(@private
)createBrowserHistory
-> in favor ofcreateBrowserRouter
createHashHistory
-> in favor ofcreateHashRouter
createMemoryHistory
-> in favor ofcreateMemoryRouter
createRouter
createStaticHandler
-> in favor of wrappercreateStaticHandler
in RR DomgetStaticContextFromError
- types
- Removed the following exports that were previously public API from
react-router
Hash
Pathname
Search
- Removed the following exports that were previously public API from
- Remove
future.v7_prependBasename
from the internalized@remix-run/router
package (#11726) - Remove
future.v7_throwAbortReason
from internalized@remix-run/router
package (#11728) - Add
exports
field to all packages (#11675) - Renamed
RemixContext
toFrameworkContext
(#11705) - Update the minimum React version to 18 (#11689)
PrefetchPageDescriptor
replaced byPageLinkDescriptor
(#11960)- Remove the
future.v7_partialHydration
flag (#11725)- This also removes the
<RouterProvider fallbackElement>
prop- To migrate, move the
fallbackElement
to ahydrateFallbackElement
/HydrateFallback
on 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.navigation
was populated during the initial load - With
future.v7_partialHydration
,state.navigation
remains in an"idle"
state during the initial load
- Without
- This also removes the
- Remove
future.v7_relativeSplatPath
future flag (#11695) - Remove remaining future flags (#11820)
- React Router
v7_skipActionErrorRevalidation
- Remix
v3_fetcherPersist
,v3_relativeSplatPath
,v3_throwAbortReason
- React Router
- Rename
createRemixStub
tocreateRoutesStub
(#11692) - Remove
@remix-run/router
deprecateddetectErrorBoundary
option in favor ofmapRouteProperties
(#11751) - Add
react-router/dom
subpath export to properly enablereact-dom
as 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 breakcreateMemoryRouter
use cases in non-DOM environments - DOM environments should import from
react-router/dom
to 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_fetcherPersist
flag (#11731) - Allow returning
undefined
from loaders and actions (#11680, #12057) - Use
createRemixRouter
/RouterProvider
inentry.client
instead ofRemixBrowser
(#11469) - Remove the deprecated
json
utility (#12146)- You can use
Response.json
if you still need to construct JSON responses in your app
- You can use
- Remove
future.v3_singleFetch
flag (#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
exports
field to all packages (#11675) - No longer re-export APIs from
react-router
through different runtime/adapter packages (#11702) - For Remix consumers migrating to React Router, the
crypto
global from the Web Crypto API is now required when using cookie and session APIs- This means that the following APIs are provided from
react-router
rather than platform-specific packages: (#11837)createCookie
createCookieSessionStorage
createMemorySessionStorage
createSessionStorage
- For consumers running older versions of Node, the
installGlobals
function from@remix-run/node
has been updated to defineglobalThis.crypto
, using Node'srequire('node:crypto').webcrypto
implementation - Since platform-specific packages no longer need to implement this API, the following low-level APIs have been removed:
createCookieFactory
createSessionStorageFactory
createCookieSessionStorageFactory
createMemorySessionStorageFactory
- This means that the following APIs are provided from
- Consolidate types previously duplicated across
@remix-run/router
,@remix-run/server-runtime
, and@remix-run/react
now that they all live inreact-router
(#12177)- Examples:
LoaderFunction
,LoaderFunctionArgs
,ActionFunction
,ActionFunctionArgs
,DataFunctionArgs
,RouteManifest
,LinksFunction
,Route
,EntryRoute
- The
RouteManifest
type used by the "remix" code is now slightly stricter because it is using the former@remix-run/router
RouteManifest
Record<string, Route> -> Record<string, Route | undefined>
- Removed
AppData
type in favor of inliningunknown
in 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
useFetcher
previously 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
cookie
dependency to^1.0.1
- please see the release notes for any breaking changes (#12172) @react-router/cloudflare
- For Remix consumers migrating to React Router, all exports from@remix-run/cloudflare-pages
are now provided for React Router consumers in the@react-router/cloudflare
package. There is no longer a separate package for Cloudflare Pages. (#11801)@react-router/cloudflare
- The@remix-run/cloudflare-workers
package has been deprecated. Remix consumers migrating to React Router should use the@react-router/cloudflare
package directly. For guidance on how to use@react-router/cloudflare
within a Cloudflare Workers context, refer to the Cloudflare Workers template. (#11801)@react-router/dev
- For Remix consumers migrating to React Router, thevitePlugin
andcloudflareDevProxyVitePlugin
exports have been renamed and moved. (#11904)@react-router/dev
- For Remix consumers migrating to React Router who used the Vite plugin'sbuildEnd
hook, the resolvedreactRouterConfig
object no longer contains apublicPath
property since this belongs to Vite, not React Router (#11575)@react-router/dev
- For Remix consumers migrating to React Router, the Vite plugin'smanifest
option has been removed (#11573)@react-router/dev
- Update defaultisbot
version to v5 and drop support forisbot@3
(#11770)- If you have
isbot@4
orisbot@5
in yourpackage.json
:- You do not need to make any changes
- If you have
isbot@3
in yourpackage.json
and you have your ownentry.server.tsx
file in your repo- You do not need to make any changes
- You can upgrade to
isbot@5
independent of the React Router v7 upgrade
- If you have
isbot@3
in yourpackage.json
and you do not have your ownentry.server.tsx
file in your repo- You are using the internal default entry provided by React Router v7 and you will need to upgrade to
isbot@5
in 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.json
andbuild/server/.vite/manifest.json
instead ofbuild/.vite/client-manifest.json
andbuild/.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/.vite
directory to avoid accidentally serving them in production, particularly from the client build. This was later improved with additional logic that deleted these Vite manifest files at the end of the build process unless Vite'sbuild.manifest
had been enabled within the app's Vite config. This greatly reduced the risk of accidentally serving the Vite manifests in production since they're only present when explicitly asked for. As a result, we can now assume that consumers will know that they need to manage these additional files themselves, and React Router can safely generate a more standard Vite build output.
- Originally the Remix Vite plugin moved all Vite manifests to a root-level
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 duplicateRouterProvider
implementations (#11679)react-router
- Stabilizeunstable_dataStrategy
(#11969)react-router
- Stabilizeunstable_patchRoutesOnNavigation
(#11970)react-router
- Add prefetching support toLink
/NavLink
when using Remix SSR (#11402)react-router
- EnhanceScrollRestoration
so it can restore properly on an SSR'd document load (#11401)@react-router/dev
- Add support for theprerender
config in the React Router vite plugin, to support existing SSG use-cases (#11539)@react-router/dev
- Remove internalentry.server.spa.tsx
implementation which was not compatible with the Single Fetch async hydration approach (#11681)@react-router/serve
: Updateexpress.static
configurations to support newprerender
API (#11547)- Assets in the
build/client/assets
folder are served as before, with a 1-year immutableCache-Control
header - Static files outside of assets, such as pre-rendered
.html
and.data
files are not served with a specificCache-Control
header .data
files 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=0
to.data
files
- For some reason, when adding this via
- Assets in the
- Replace
substr
withsubstring
(#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)
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
release-next
Date: 2024-11-06
- 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.
- Log deprecation warnings for v7 flags (#11750)
- Add deprecation warnings to
json
/defer
in favor of returning raw objects- These methods will be removed in React Router v7
- Add deprecation warnings to
- Update JSDoc URLs for new website structure (add /v6/ segment) (#12141)
Full Changelog: v6.27.0...v6.28.0
dev
release-next
Date: 2024-10-11
This release stabilizes a handful of "unstable" APIs in preparation for the pending React Router v7 release (see these posts for more info):
unstable_dataStrategy
→dataStrategy
(createBrowserRouter
and friends) (Docs)unstable_patchRoutesOnNavigation
→patchRoutesOnNavigation
(createBrowserRouter
and friends) (Docs)unstable_flushSync
→flushSync
(useSubmit
,fetcher.load
,fetcher.submit
) (Docs)unstable_viewTransition
→viewTransition
(<Link>
,<Form>
,useNavigate
,useSubmit
) (Docs)
- Stabilize the
unstable_flushSync
option for navigations and fetchers (#11989) - Stabilize the
unstable_viewTransition
option for navigations and the correspondingunstable_useViewTransitionState
hook (#11989) - Stabilize
unstable_dataStrategy
(#11974) - Stabilize
unstable_patchRoutesOnNavigation
(#11973)- Add new
PatchRoutesOnNavigationFunctionArgs
type for convenience (#11967)
- Add new
- Fix bug when submitting to the current contextual route (parent route with an index child) when an
?index
param already exists from a prior submission (#12003) - Fix
useFormAction
bug - when removing?index
param it would not keep other non-Remixindex
params (#12003) - Fix bug with fetchers not persisting
preventScrollReset
through redirects during concurrent fetches (#11999) - Avoid unnecessary
console.error
on fetcher abort due to back-to-back revalidation calls (#12050) - Fix bugs with
partialHydration
when hydrating with errors (#12070) - Remove internal cache to fix issues with interrupted
patchRoutesOnNavigation
calls (#12055)⚠️ This may be a breaking change if you were relying on this behavior in theunstable_
API- We used to cache in-progress calls to
patchRoutesOnNavigation
internally 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
patch
short circuiting if a navigation was interrupted (and therequest.signal
aborted) since the first invocation'spatch
would 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
- Remove internal
discoveredRoutes
FIFO queue fromunstable_patchRoutesOnNavigation
(#11977)⚠️ This may be a breaking change if you were relying on this behavior in theunstable_
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
- Fix types for
RouteObject
withinPatchRoutesOnNavigationFunction
'spatch
method so it doesn't expect agnostic route objects passed topatch
(#11967) - Expose errors thrown from
patchRoutesOnNavigation
directly touseRouteError
instead of wrapping them in a 400ErrorResponse
instance (#12111)
Full Changelog: v6.26.2...v6.27.0
Date: 2024-09-09
- Update the
unstable_dataStrategy
API to allow for more advanced implementations (#11943)⚠️ If you have already adoptedunstable_dataStrategy
, please review carefully as this includes breaking changes to this API- Rename
unstable_HandlerResult
tounstable_DataStrategyResult
- Change the return signature of
unstable_dataStrategy
from 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
handlerOverride
instead of returning aDataStrategyResult
- The return value (or thrown error) from your
handlerOverride
will be wrapped up into aDataStrategyResult
and 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 theDataStrategyResult
type - If you are manually filling your results object from within your
handlerOverride
, then you will need to assign aDataStrategyResult
as 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
fetcherKey
parameter tounstable_dataStrategy
to allow differentiation from navigational and fetcher calls
- Preserve opted-in view transitions through redirects (#11925)
- Preserve pending view transitions through a router revalidation call (#11917)
- Fix blocker usage when
blocker.proceed
is called quickly/synchronously (#11930)
Full Changelog: v6.26.1...v6.26.2
Date: 2024-08-15
- Rename
unstable_patchRoutesOnMiss
tounstable_patchRoutesOnNavigation
to match new behavior (#11888) - Update
unstable_patchRoutesOnNavigation
logic so that we call the method when we match routes with dynamic param or splat segments in case there exists a higher-scoring static route that we've not yet discovered (#11883)- We also now leverage an internal FIFO queue of previous paths we've already called
unstable_patchRoutesOnNavigation
against 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
Date: 2024-08-01
- Add a new
replace(url, init?)
alternative toredirect(url, init?)
that performs ahistory.replaceState
instead of ahistory.pushState
on 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
/headers
without forcing the serialization of data into aResponse
instance - This allows for more advanced serialization tactics via
unstable_dataStrategy
such as serializing viaturbo-stream
in Remix Single Fetch ⚠️ This removes thestatus
field fromHandlerResult
- If you need to return a specific
status
fromunstable_dataStrategy
you should instead do so viaunstable_data()
- If you need to return a specific
- Fix internal cleanup of interrupted fetchers to avoid invalid revalidations on navigations (#11839)
- Fix initial hydration behavior when using
future.v7_partialHydration
along withunstable_patchRoutesOnMiss
(#11838)- During initial hydration,
router.state.matches
will now include any partial matches so that we can render ancestorHydrateFallback
components
- During initial hydration,
Full Changelog: v6.25.1...v6.26.0
Date: 2024-07-17
- Memoize some
RouterProvider
internals to reduce unnecessary re-renders (#11803)
Full Changelog: v6.25.0...v6.25.1
Date: 2024-07-16
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/5xx
Response
will not trigger a revalidation by default - This also stabilizes
shouldRevalidate
'sunstable_actionStatus
parameter toactionStatus
- Stabilize
future.unstable_skipActionErrorRevalidation
asfuture.v7_skipActionErrorRevalidation
(#11769)
- Fix regression and properly decode paths inside
useMatch
so matches/params reflect decoded params (#11789) - Fix bubbling of errors thrown from
unstable_patchRoutesOnMiss
(#11786) - Fix hydration in SSR apps using
unstable_patchRoutesOnMiss
that matched a splat route on the server (#11790)
Full Changelog: v6.24.1...v6.25.0
Date: 2024-07-03
- Remove
polyfill.io
reference from warning message because the domain was sold and has since been determined to serve malware (#11741) - Export
NavLinkRenderProps
type for easier typing of customNavLink
callback (#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.routes
identity/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
Date: 2024-06-24
We're really excited to release our new API for "Lazy Route Discovery" in v6.24.0
! For some background information, please check out the original RFC. The tl;dr; is that ever since we introduced the Data APIs in v6.4 via <RouterProvider>
, we've been a little bummed that one of the tradeoffs was the lack of a compelling code-splitting story mirroring what we had in the <BrowserRouter>
/<Routes>
apps. We took a baby-step towards improving that story with route.lazy
in v6.9.0
, but with v6.24.0
we've gone the rest of the way.
With "Fog of War", you can now load portions of the route tree lazily via the new unstable_patchRoutesOnMiss
option passed to createBrowserRouter
(and it's memory/hash counterparts). This gives you a way to hook into spots where React Router is unable to match a given path and patch new routes into the route tree during the navigation (or fetcher call).
Here's a very small example, but please refer to the documentation for more information and use cases:
const router = createBrowserRouter(
[
{
id: "root",
path: "/",
Component: RootComponent,
},
],
{
async unstable_patchRoutesOnMiss({ path, patch }) {
if (path === "/a") {
// Load the `a` route (`{ path: 'a', Component: A }`)
let route = await getARoute();
// Patch the `a` route in as a new child of the `root` route
patch("root", [route]);
}
},
}
);
- Add support for Lazy Route Discovery (a.k.a. "Fog of War") (#11626)
- Fix
fetcher.submit
types - remove incorrectnavigate
/fetcherKey
/unstable_viewTransition
options because they are only relevant foruseSubmit
(#11631) - Allow falsy
location.state
values passed to<StaticRouter>
(#11495)
Full Changelog: v6.23.1...v6.24.0
Date: 2024-05-10
- Allow
undefined
to be resolved through<Await>
(#11513) - Add defensive
document
check when checking fordocument.startViewTransition
availability (#11544) - Change the
react-router-dom/server
import back toreact-router-dom
instead ofindex.ts
(#11514) @remix-run/router
- Supportunstable_dataStrategy
onstaticHandler.queryRoute
(#11515)
Full Changelog: v6.23.0...v6.23.1
Date: 2024-04-23
The new unstable_dataStrategy
API is a low-level API designed for advanced use-cases where you need to take control over the data strategy for your loader
/action
functions. The default implementation is today's behavior, to fetch all loaders in parallel, but this option allows users to implement more advanced data flows including Remix "Single Fetch", user-land middleware/context APIs, automatic loader caching, and more. Please see the docs for more information.
Note: This is a low-level API intended for advanced use-cases. This overrides React Router's internal handling of loader
/action
execution, and if done incorrectly will break your app code. Please use with caution and perform the appropriate testing.
Currently, all active loader
's revalidate after any action
submission, regardless of the action
result. However, in the majority of cases a 4xx
/5xx
response from an action
means that no data was actually changed and the revalidation is unnecessary. We've introduced a new future.unstable_skipActionErrorRevalidation
flag that changes the behavior here, and we plan to make this the default in future version of React Router.
With this flag enabled, action
's that return/throw a 4xx
/5xx
response status will no longer automatically revalidate. If you need to revalidate after a 4xx
/5xx
result with this flag enabled, you can still do that via returning true
from shouldRevalidate
- which now also receives a new unstable_actionStatus
argument alongside actionResult
so you can make decision based on the status of the action
response without having to encode it into the action data.
- Add a new
unstable_dataStrategy
configuration option (#11098, #11377) @remix-run/router
- Add a newfuture.unstable_skipActionRevalidation
future flag (#11098)@remix-run/router
- SSR: Added a newskipLoaderErrorBubbling
options to thestaticHandler.query
method to disable error bubbling by the static handler for use in Remix's Single Fetch implementation (#11098, (#11377))
Full Changelog: v6.22.3...v6.23.0
Date: 2024-03-07
- Fix a
future.v7_partialHydration
bug that would re-run loaders below the boundary on hydration if SSR loader errors bubbled to a parent boundary (#11324) - Fix a
future.v7_partialHydration
bug that would consider the router uninitialized if a route did not have a loader (#11325)
Full Changelog: v6.22.2...v6.22.3
Date: 2024-02-28
- Preserve hydrated errors during partial hydration runs (#11305)
Full Changelog: v6.22.1...v6.22.2
Date: 2024-02-16
- Fix encoding/decoding issues with pre-encoded dynamic parameter values (#11199)
Full Changelog: v6.22.0...v6.22.1
Date: 2024-02-01
In 2021, the HTTP Archive launched the Core Web Vitals Technology Report dashboard:
By combining the powers of real-user experiences in the Chrome UX Report 26 (CrUX) dataset with web technology detections in HTTP Archive 30, we can get a glimpse into how architectural decisions like choices of CMS platform or JavaScript framework play a role in sites’ CWV performance.
They use a tool called wappalyzer
to identify what technologies a given website is using by looking for certain scripts, global JS variables, or other identifying characteristics. For example, for Remix applications, they look for the global __remixContext
variable to identify that a website is using Remix.
It was brought to our attention that React Router was unable to be reliably identified because there are no identifying global aspects. They are currently looking for external scripts with react-router
in the name. This will identify sites using React Router from a CDN such as unpkg
- but it will miss the vast majority of sites that are installing React Router from the npm registry and bundling it into their JS files. This results in drastically under-reporting the usage of React Router on the web.
Starting with version 6.22.0
, sites using react-router-dom
will begin adding a window.__reactRouterVersion
variable that will be set to a string value of the SemVer major version number (i.e., window.__reactRouterVersion = "6";
) so that they can be properly identified.
- Include a
window.__reactRouterVersion
for CWV Report detection (#11222) - Add a
createStaticHandler
future.v7_throwAbortReason
flag to throwrequest.signal.reason
(defaults to aDOMException
) when a request is aborted instead of anError
such asnew Error("query() call aborted: GET /path")
(#11104)- Please note that
DOMException
was added in Node v17 so you will not get aDOMException
on Node 16 and below.
- Please note that
- Respect the
ErrorResponse
status code if passed togetStaticContextFormError
(#11213)
Full Changelog: v6.21.3...v6.22.0
Date: 2024-01-18
- Fix
NavLink
isPending
when abasename
is used (#11195) - Remove leftover
unstable_
prefix fromBlocker
/BlockerFunction
types (#11187)
Full Changelog: v6.21.2...v6.21.3
Date: 2024-01-11
- Leverage
useId
for 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
Date: 2023-12-21
- Fix bug with
route.lazy
not working correctly on initial SPA load whenv7_partialHydration
is specified (#11121) - Fix bug preventing revalidation from occurring for persisted fetchers unmounted during the
submitting
phase (#11102) - De-dup relative path logic in
resolveTo
(#11097)
Full Changelog: v6.21.0...v6.21.1
Date: 2023-12-13
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.
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)
- Add a new
future.v7_relativeSplatPath
flag to implement a breaking bug fix to relative routing when inside a splat route. (#11087) - Add a new
future.v7_partialHydration
future flag that enables partial hydration of a data router when Server-Side Rendering (#11033)
- Properly handle falsy error values in
ErrorBoundary
's (#11071) - Catch and bubble errors thrown when trying to unwrap responses from
loader
/action
functions (#11061) - Fix
relative="path"
issue when renderingLink
/NavLink
outside of matched routes (#11062)
Full Changelog: v6.20.1...v6.21.0
Date: 2023-12-01
- Revert the
useResolvedPath
fix 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.0
and6.20.0
. If you are upgrading from6.18.0
or earlier, you would not have been impacted by this fix.
Full Changelog: v6.20.0...v6.20.1
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.
- Export the
PathParam
type from the public API (#10719)
- Do not revalidate unmounted fetchers when
v7_fetcherPersist
is enabled (#11044) - Fix bug with
resolveTo
path 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_getPathContributingMatches
export from@remix-run/router
since we no longer need this in thereact-router
/react-router-dom
layers
- This is a follow up to #10983 to handle the few other code paths using
Full Changelog: v6.19.0...v6.20.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.
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();
}
- Add
unstable_flushSync
option touseNavigate
/useSubmit
/fetcher.load
/fetcher.submit
to opt-out ofReact.startTransition
and intoReactDOM.flushSync
for state updates (#11005) - Remove the
unstable_
prefix from theuseBlocker
hook 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_usePrompt
due to differences in how browsers handlewindow.confirm
that prevent React Router from guaranteeing consistent/correct behavior
- We do not plan to remove the prefix from
-
Fix
useActionData
so it returns proper contextual action data and not any action data in the tree (#11023) -
Fix bug in
useResolvedPath
that 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.
-
Fix issue where a changing fetcher
key
in auseFetcher
that remains mounted wasn't getting picked up (#11009) -
Fix
useFormAction
which was incorrectly inheriting the?index
query param from child routeaction
submissions (#11025) -
Fix
NavLink
active
logic whento
location has a trailing slash (#10734) -
Fix types so
unstable_usePrompt
can accept aBlockerFunction
in 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
Date: 2023-10-31
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
useFetchers
so that they can be looked up bykey
Form
anduseSubmit
now support optionalnavigate
/fetcherKey
props/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
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 anidle
state - 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
idle
state- They will remain exposed via
useFetchers
while 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
- Add fetcher
key
APIs andnavigate=false
options (#10960) - Add
future.v7_fetcherPersist
flag (#10962) - Add support for optional path segments in
matchPath
(#10768)
- Fix the
future
prop onBrowserRouter
,HashRouter
andMemoryRouter
so that it accepts aPartial<FutureConfig>
instead of requiring all flags to be included (#10962) - Fix
router.getFetcher
/router.deleteFetcher
type definitions which incorrectly specifiedkey
as an optional parameter (#10960)
Full Changelog: 6.17.0...6.18.0
Date: 2023-10-16
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.
- Add support for view transitions (#10916)
- Log a warning and fail gracefully in
ScrollRestoration
whensessionStorage
is unavailable (#10848) - Fix
RouterProvider
future
prop 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
ErrorResponse
type to avoid leaking internal field (#10876)
Full Changelog: 6.16.0...6.17.0
Date: 2023-09-13
- In order to move towards stricter TypeScript support in the future, we're aiming to replace current usages of
any
withunknown
on 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 toany
in React Router and are overridden withunknown
in Remix. In React Router v7 we plan to move these tounknown
as a breaking change. (#10843)Location
now accepts a generic for thelocation.state
valueActionFunctionArgs
/ActionFunction
/LoaderFunctionArgs
/LoaderFunction
now accept a generic for thecontext
parameter (only used in SSR usages viacreateStaticHandler
)- The return type of
useMatches
(now exported asUIMatch
) accepts generics formatch.data
andmatch.handle
- both of which were already set tounknown
- Move the
@private
class exportErrorResponse
to anUNSAFE_ErrorResponseImpl
export since it is an implementation detail and there should be no construction ofErrorResponse
instances in userland. This frees us up to export atype ErrorResponse
which correlates to an instance of the class viaInstanceType
. Userland code should only ever be usingErrorResponse
as a type and should be type-narrowing viaisRouteErrorResponse
. (#10811) - Export
ShouldRevalidateFunctionArgs
interface (#10797) - Removed private/internal APIs only required for the Remix v1 backwards compatibility layer and no longer needed in Remix v2 (
_isFetchActionRedirect
,_hasFetcherDoneAnything
) (#10715)
- Properly encode rendered URIs in server rendering to avoid hydration errors (#10769)
- Add method/url to error message on aborted
query
/queryRoute
calls (#10793) - Fix a race-condition with loader/action-thrown errors on
route.lazy
routes (#10778) - Fix type for
actionResult
on the arguments object passed toshouldRevalidate
(#10779)
Full Changelog: v6.15.0...v6.16.0
Date: 2023-08-10
- Add's a new
redirectDocument()
function which allows users to specify that a redirect from aloader
/action
should trigger a document reload (viawindow.location
) instead of attempting to navigate to the redirected location via React Router (#10705)
- Ensure
useRevalidator
is 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
URLSearchParams
and theuseSearchParams
hook (#10620) - Reorder effects in
unstable_usePrompt
to 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
queryRoute
that was not always identifying thrownResponse
instances (#10717) react-router-native
: Update@ungap/url-search-params
dependency from^0.1.4
to^0.2.2
(#10590)
Full Changelog: v6.14.2...v6.15.0
Date: 2023-07-17
- Add missing
<Form state>
prop to populatehistory.state
on submission navigations (#10630) - Trigger an error if a
defer
promise resolves/rejects withundefined
in 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.lazy
to prohibit returning an empty object (#10634) - SSR: Support proper hydration of
Error
subclasses such asReferenceError
/TypeError
(#10633)
Full Changelog: v6.14.1...v6.14.2
Date: 2023-06-30
- Fix loop in
unstable_useBlocker
when used with an unstable blocker function (#10652) - Fix issues with reused blockers on subsequent navigations (#10656)
- Updated dependencies:
@remix-run/[email protected]
Full Changelog: v6.14.0...v6.14.1
Date: 2023-06-23
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"
}
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.
- Add support for
application/json
andtext/plain
encodings foruseSubmit
/fetcher.submit
. To reflect these additional types,useNavigation
/useFetcher
now also containnavigation.json
/navigation.text
andfetcher.json
/fetcher.text
which include the json/text submission if applicable. (#10413)
- When submitting a form from a
submitter
element, prefer the built-innew FormData(form, submitter)
instead of the previous manual approach in modern browsers (those that support the newsubmitter
parameter) (#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/replaceState
before updating React Router state (instead of after) so thatwindow.location
matchesuseLocation
during synchronous React 17 rendering (#10448)⚠️ Note: generally apps should not be relying onwindow.location
and should always referenceuseLocation
when possible, aswindow.location
will not be in sync 100% of the time (due topopstate
events, concurrent mode, etc.)
- Avoid calling
shouldRevalidate
for fetchers that have not yet completed a data load (#10623) - Strip
basename
from thelocation
provided to<ScrollRestoration getKey>
to match theuseLocation
behavior (#10550) - Strip
basename
from locations provided tounstable_useBlocker
functions to match theuseLocation
behavior (#10573) - Fix
unstable_useBlocker
key issues inStrictMode
(#10573) - Fix
generatePath
when passed a numeric0
value parameter (#10612) - Fix
tsc --skipLibCheck:false
issues on React 17 (#10622) - Upgrade
typescript
to 5.1 (#10581)
Full Changelog: v6.13.0...v6.14.0
Date: 2023-06-14
6.13.0
is really a patch release in spirit but comes with a SemVer minor bump since we added a new future flag.
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
.
- Move
React.startTransition
usage behinds a future flag (#10596)
- Work around webpack/terser
React.startTransition
minification bug in production mode (#10588)
Full Changelog: v6.12.1...v6.13.0
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.
- Adjust feature detection of
React.startTransition
to fix webpack + react 17 compilation error (#10569)
Full Changelog: v6.12.0...v6.12.1
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.
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.
- Wrap internal router state updates with
React.startTransition
(#10438)
- Allow fetcher revalidations to complete if submitting fetcher is deleted (#10535)
- Re-throw
DOMException
(DataCloneError
) when attempting to perform aPUSH
navigation with non-serializable state. (#10427) - Ensure revalidations happen when hash is present (#10516)
- Upgrade
jest
andjsdom
(#10453) - Updated dependencies:
@remix-run/[email protected]
(Changelog)
Full Changelog: v6.11.2...v6.12.0
Date: 2023-05-17
- Fix
basename
duplication in descendant<Routes>
inside a<RouterProvider>
(#10492) - Fix bug where initial data load would not kick off when hash is present (#10493)
- Export
SetURLSearchParams
type (#10444) - Fix Remix HMR-driven error boundaries by properly reconstructing new routes and
manifest
in_internalSetRoutes
(#10437)
Full Changelog: v6.11.1...v6.11.2
Date: 2023-05-03
- Fix usage of
Component
API within descendant<Routes>
(#10434) - Fix bug when calling
useNavigate
from<Routes>
inside a<RouterProvider>
(#10432) - Fix usage of
<Navigate>
in strict mode when using a data router (#10435) - Fix
basename
handling 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
Date: 2023-04-28
- Enable
basename
support inuseFetcher
(#10336)- If you were previously working around this issue by manually prepending the
basename
then you will need to remove the manually prependedbasename
from yourfetcher
calls (fetcher.load('/basename/route') -> fetcher.load('/route')
)
- If you were previously working around this issue by manually prepending the
- Updated dependencies:
@remix-run/[email protected]
(Changelog)
- When using a
RouterProvider
,useNavigate
/useSubmit
/fetcher.submit
are now stable across location changes, since we can handle relative routing via the@remix-run/router
instance 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
action
submissions orrouter.revalidate
calls (#10344) - Fix inadvertent re-renders when using
Component
instead ofelement
on a route definition (#10287) - Fail gracefully on
<Link to="//">
and other invalid URL values (#10367) - Switched from
useSyncExternalStore
touseState
for internal@remix-run/router
router state syncing in<RouterProvider>
. We found some subtle bugs where router state updates got propagated before other normaluseState
updates, which could lead to foot guns inuseEffect
calls. (#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>
whenRouterProvider
errors existed (#10374) - Fix detection of
useNavigate
in the render cycle by setting theactiveRef
in a layout effect, allowing thenavigate
function to be passed to child components and called in auseEffect
there (#10394) - Allow
useRevalidator()
to resolve a loader-driven error boundary scenario (#10369) - Enhance
LoaderFunction
/ActionFunction
return type to preventundefined
from being a valid return value (#10267) - Ensure proper 404 error on
fetcher.load
call to a route without aloader
(#10345) - Decouple
AbortController
usage 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
Date: 2023-03-29
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.
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_normalizeFormMethod
is unspecified or set tofalse
(default v6 behavior),useNavigation().formMethod
is lowercaseuseFetcher().formMethod
is lowercase
- When
future.v7_normalizeFormMethod === true
:useNavigation().formMethod
is UPPERCASEuseFetcher().formMethod
is UPPERCASE
- Fix
createStaticHandler
to also check forErrorBoundary
on routes in addition toerrorElement
(#10190) - Fix route ID generation when using Fragments in
createRoutesFromElements
(#10193) - Provide fetcher submission to
shouldRevalidate
if the fetcher action redirects (#10208) - Properly handle
lazy()
errors during router initialization (#10201) - Remove
instanceof
check forDeferredData
to be resilient to ESM/CJS boundaries in SSR bundling scenarios (#10247) - Update to latest
@remix-run/[email protected]
(#10216)
Full Changelog: v6.9.0...v6.10.0
Date: 2023-03-10
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() { ... }
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.
- Add support for
route.Component
/route.ErrorBoundary
properties (#10045) - Add support for
route.lazy
(#10045)
- Improve memoization for context providers to avoid unnecessary re-renders (#9983)
- Fix
generatePath
incorrectly 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
Date: 2023-02-27
- 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
StaticRouterProvider
serialized hydration data (#10068) - Fix
useBlocker
to returnIDLE_BLOCKER
during SSR (#10046) - Ensure status code and headers are maintained for
defer
loader responses increateStaticHandler
'squery()
method (#10077) - Change
invariant
to anUNSAFE_invariant
export since it's only intended for internal use (#10066)
Full Changelog: v6.8.1...v6.8.2
Date: 2023-02-06
- 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
Link
component (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
Date: 2023-01-26
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 */}
- Fixes 2 separate issues for revalidating fetcher
shouldRevalidate
calls (#9948)- The
shouldRevalidate
function was only being called for explicit revalidation scenarios (after a mutation, manualuseRevalidator
call, or anX-Remix-Revalidate
header used for cookie setting in Remix). It was not properly being called on implicit revalidation scenarios that also apply to navigationloader
revalidation, 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.load
URL (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
preventScrollReset
on<fetcher.Form>
(#9963) - Fix navigation for hash routers on manual URL changes (#9980)
- Use
pagehide
instead ofbeforeunload
for<ScrollRestoration>
. This has better cross-browser support, specifically on Mobile Safari. (#9945) - Do not short circuit on hash change only mutation submissions (#9944)
- Remove
instanceof
check fromisRouteErrorResponse
to avoid bundling issues on the server (#9930) - Detect when a
defer
call only contains critical data and remove theAbortController
(#9965) - Send the name as the value when url-encoding
File
FormData
entries (#9867) react-router-dom-v5-compat
- Fix SSRuseLayoutEffect
console.error
when usingCompatRouter
(#9820)
Full Changelog: v6.7.0...v6.8.0
Date: 2023-01-18
- Add
unstable_useBlocker
/unstable_usePrompt
hooks for blocking navigations within the app's location origin (#9709, #9932) - Add
preventScrollReset
prop to<Form>
(#9886)
- Added pass-through event listener options argument to
useBeforeUnload
(#9709) - Fix
generatePath
when optional params are present (#9764) - Update
<Await>
to acceptReactNode
as 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
jsdom
bug workaround in tests (#9824)
Full Changelog: v6.6.2...v6.7.0
Date: 2023-01-09
- Ensure
useId
consistency during SSR (#9805)
Full Changelog: v6.6.1...v6.6.2
Date: 2022-12-23
- Include submission info in
shouldRevalidate
on action redirects (#9777, #9782) - Reset
actionData
on action redirect to current location (#9772)
Full Changelog: v6.6.0...v6.6.1
Date: 2022-12-21
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.
- Remove
unstable_
prefix fromcreateStaticHandler
/createStaticRouter
/StaticRouterProvider
(#9738) - Add
useBeforeUnload()
hook (#9664)
- Support uppercase
<Form method>
anduseSubmit
method values (#9664) - Fix
<button formmethod>
form submission overriddes (#9664) - Fix explicit
replace
on submissions andPUSH
on submission to new paths (#9734) - Prevent
useLoaderData
usage inerrorElement
(#9735) - Proper hydration of
Error
objects 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
Date: 2022-12-16
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
- Allows optional routes and optional static segments (#9650)
- 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 theuseParams
call 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
headers
onloader
request
's after SSR documentaction
request (#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
Date: 2022-12-07
- Fix requests sent to revalidating loaders so they reflect a
GET
request (#9680) - Remove
instanceof Response
checks in favor ofisResponse
(#9690) - Fix
URL
creation in Cloudflare Pages or other non-browser-environments (#9682, #9689) - Add
requestContext
support 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
Date: 2022-11-30
- Throw an error if an
action
/loader
function returnsundefined
as revalidations need to know whether the loader has previously been executed.undefined
also causes issues during SSR stringification for hydration. You should always ensure yourloader
/action
returns a value, and you may returnnull
if 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
basename
in static data routers (#9591) - Enhanced
ErrorResponse
bodies to contain more descriptive text in internal 403/404/405 scenarios - Fix issues with encoded characters in
NavLink
and descendant<Routes>
(#9589, #9647) - Properly serialize/deserialize
ErrorResponse
instances when using built-in hydration (#9593) - Support
basename
in static data routers (#9591) - Updated dependencies:
@remix-run/[email protected]
[email protected]
Full Changelog: v6.4.3...v6.4.4
Date: 2022-11-01
- Generate correct
<a href>
values when usingcreateHashRouter
(#9409) - Better handle encoding/matching with special characters in URLs and route paths (#9477, #9496)
- Generate correct
formAction
pathnames when anindex
route also has apath
(#9486) - Respect
relative=path
prop onNavLink
(#9453) - Fix
NavLink
behavior for root urls (#9497) useRoutes
should be able to returnnull
when passinglocationArg
(#9485)- Fix
initialEntries
type increateMemoryRouter
(#9498) - Support
basename
and relative routing inloader
/action
redirects (#9447) - Ignore pathless layout routes when looking for proper submission
action
function (#9455) - Add UMD build for
@remix-run/router
(#9446) - Fix
createURL
in local file execution in Firefox (#9464)
Full Changelog: v6.4.2...v6.4.3
Date: 2022-10-06
- Respect
basename
inuseFormAction
(#9352) - Fix
IndexRouteObject
andNonIndexRouteObject
types to makehasErrorElement
optional (#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
/RouteProps
types to surface the error in TypeScript. (#9366)
Full Changelog: v6.4.1...v6.4.2
Date: 2022-09-22
- Preserve state from
initialEntries
(#9288) - Preserve
?index
for fetcher get submissions to index routes (#9312)
Full Changelog: v6.4.0...v6.4.1
Date: 2022-09-13
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
loader
and mutate with a Routeaction
- Handle errors with Route
errorElement
- Defer non-critical data with
defer
andAwait
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
defer
andAwait
- Manage scroll position with
<ScrollRestoration>
- Perform path-relative navigations with
<Link relative="path">
(#9160)
- Path resolution is now trailing slash agnostic (#8861)
useLocation
returns 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
Date: 2022-03-31
- 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
Date: 2022-02-28
- 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
Date: 2021-12-17
- This release updates the internal
history
dependency to5.2.0
.
Full Changelog: v6.2.0...v6.2.1
Date: 2021-12-17
- We now use statically analyzable CJS exports. This enables named imports in Node ESM scripts (See the commit).
- Fixed the
RouteProps
element
type, which should be aReactNode
(#8473) - Fixed a bug with
useOutlet
for top-level routes (#8483)
Full Changelog: v6.1.1...v6.2.0
Date: 2021-12-11
- In v6.1.0 we inadvertently shipped a new, undocumented API that will likely introduce bugs (#7586). We have flagged
HistoryRouter
asunstable_HistoryRouter
, as this API will likely need to change before a new major release.
Full Changelog: v6.1.0...v6.1.1
Date: 2021-12-10
<Outlet>
can now receive acontext
prop. This value is passed to child routes and is accessible via the newuseOutletContext
hook. See the API docs for details. (#8461)<NavLink>
can now receive a child function for access to its props. (#8164)- Improved TypeScript signature for
useMatch
andmatchPath
. For example, when you calluseMatch("foo/:bar/:baz")
, the path is parsed and the return type will bePathMatch<"bar" | "baz">
. (#8030)
- 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
Date: 2021-11-09
- Added the
reloadDocument
prop to<Link>
. This allows<Link>
to function like a normal anchor tag by reloading the document after navigation while maintaining the relativeto
resolution (#8283)
Full Changelog: v6.0.1...v6.0.2
Date: 2021-11-05
- 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
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.