react-router
)@react-router/*
)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:
v6.28.0...v7.0.0
Date: 2024-11-21
react-router-dom
, @remix-run/react
, @remix-run/server-runtime
, and @remix-run/router
have been collapsed into the react-router
package
react-router-dom
is still published in v7 as a re-export of everything from react-router
@remix-run/cloudflare-pages
and @remix-run/cloudflare-workers
have been collapsed into @react-router/cloudflare
package`react-router-dom-v5-compat
and react-router-native
packages are removed starting with v7Remix v2 used to re-export all common @remix-run/server-runtime
APIs through the various runtime packages (node
, cloudflare
, deno
) so that you wouldn't need an additional @remix-run/server-runtime
dependency in your package.json
. With the collapsing of packages into react-router
, these common APIs are now no longer re-exported through the runtime adapters. You should import all common APIs from react-router
, and only import runtime-specific APIs from the runtime packages:
// Runtime-specific APIs
import { createFileSessionStorage } from "@react-router/node";
// Runtime-agnostic APIs
import { redirect, useLoaderData } from "react-router";
The following APIs have been removed in React Router v7:
json
defer
unstable_composeUploadHandlers
unstable_createMemoryUploadHandler
unstable_parseMultipartFormData
React Router v7 requires the following minimum versions:
node@20
installGlobals
method to polyfill the fetch
APIreact@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:
future.v7_relativeSplatPath
future.v7_startTransition
future.v7_fetcherPersist
future.v7_normalizeFormMethod
future.v7_partialHydration
future.v7_skipActionStatusRevalidation
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 manifest
option
For Remix consumers migrating to React Router, the Vite plugin's manifest
option has been removed. The manifest
option been superseded by the more powerful buildEnd
hook since it's passed the buildManifest
argument. You can still write the build manifest to disk if needed, but you'll most likely find it more convenient to write any logic depending on the build manifest within the buildEnd
hook itself. (#11573)
If you were using the manifest
option, you can replace it with a buildEnd
hook that writes the manifest to disk like this:
// react-router.config.ts
import { type Config } from "@react-router/dev/config";
import { writeFile } from "node:fs/promises";
export default {
async buildEnd({ buildManifest }) {
await writeFile(
"build/manifest.json",
JSON.stringify(buildManifest, null, 2),
"utf-8"
);
},
} satisfies Config;
Because React 19 will have first-class support for handling promises in the render pass (via React.use
and useAction
), we are now comfortable exposing the promises for the APIs that previously returned undefined
:
useNavigate()
useSubmit()
useFetcher().load
useFetcher().submit
useRevalidator().revalidate()
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"),
+ ]),
+];
React Router now generates types for each of your route modules and passes typed props to route module component exports (#11961, #12019). You can access those types by importing them from ./+types/<route filename without extension>
.
See How To > Route Module Type Safety and Explanations > Type Safety for more details.
React Router v7 includes a new prerender
config in the vite plugin to support SSG use-cases. This will pre-render your .html
and .data
files at build time and so you can serve them statically at runtime from a running server or a CDN (#11539)
export default defineConfig({
plugins: [
reactRouter({
async prerender({ getStaticPaths }) {
let slugs = await fakeGetSlugsFromCms();
return [
...getStaticPaths(),
...slugs.map((slug) => `/product/${slug}`),
];
},
}),
tsconfigPaths(),
],
});
async function fakeGetSlugsFromCms() {
await new Promise((r) => setTimeout(r, 1000));
return ["shirt", "hat"];
}
react-router
)defer
implementation in favor of using raw promises via single fetch and turbo-stream
(#11744)
defer
AbortedDeferredError
type TypedDeferredData
UNSAFE_DeferredData
UNSAFE_DEFERRED_SYMBOL
react-router
(#11505)
@remix-run/router
react-router-dom
@remix-run/server-runtime
@remix-run/testing
react-router-dom
package is maintained to ease adoption but it simply re-exports all APIs from react-router
future.v7_startTransition
flag (#11696)future.v7_normalizeFormMethod
future flag (#11697)@remix-run/router
AgnosticDataIndexRouteObject
AgnosticDataNonIndexRouteObject
AgnosticDataRouteMatch
AgnosticDataRouteObject
AgnosticIndexRouteObject
AgnosticNonIndexRouteObject
AgnosticRouteMatch
AgnosticRouteObject
TrackedPromise
unstable_AgnosticPatchRoutesOnMissFunction
Action
-> exported as NavigationType
via react-router
Router
exported as RemixRouter
to differentiate from RR's <Router>
getToPathname
(@private
)joinPaths
(@private
)normalizePathname
(@private
)resolveTo
(@private
)stripBasename
(@private
)createBrowserHistory
-> in favor of createBrowserRouter
createHashHistory
-> in favor of createHashRouter
createMemoryHistory
-> in favor of createMemoryRouter
createRouter
createStaticHandler
-> in favor of wrapper createStaticHandler
in RR DomgetStaticContextFromError
react-router
Hash
Pathname
Search
future.v7_prependBasename
from the internalized @remix-run/router
package (#11726)future.v7_throwAbortReason
from internalized @remix-run/router
package (#11728)exports
field to all packages (#11675)RemixContext
to FrameworkContext
(#11705)PrefetchPageDescriptor
replaced by PageLinkDescriptor
(#11960)future.v7_partialHydration
flag (#11725)
<RouterProvider fallbackElement>
prop
fallbackElement
to a hydrateFallbackElement
/HydrateFallback
on your root routefuture.v7_partialHydration
(when using fallbackElement
), state.navigation
was populated during the initial loadfuture.v7_partialHydration
, state.navigation
remains in an "idle"
state during the initial loadfuture.v7_relativeSplatPath
future flag (#11695)v7_skipActionErrorRevalidation
v3_fetcherPersist
, v3_relativeSplatPath
, v3_throwAbortReason
createRemixStub
to createRoutesStub
(#11692)@remix-run/router
deprecated detectErrorBoundary
option in favor of mapRouteProperties
(#11751)react-router/dom
subpath export to properly enable react-dom
as an optional peerDependency
(#11851)
import ReactDOM from "react-dom"
in <RouterProvider>
in order to access ReactDOM.flushSync()
, since that would break createMemoryRouter
use cases in non-DOM environmentsreact-router/dom
to get the proper component that makes ReactDOM.flushSync()
available:
entry.client.tsx
:
import { HydratedRouter } from 'react-router/dom'
createBrowserRouter
/createHashRouter
:
import { RouterProvider } from "react-router/dom"
future.v7_fetcherPersist
flag (#11731)undefined
from loaders and actions (#11680, #12057)createRemixRouter
/RouterProvider
in entry.client
instead of RemixBrowser
(#11469)json
utility (#12146)
Response.json
if you still need to construct JSON responses in your app@react-router/*
)future.v3_singleFetch
flag (#11522)installGlobals()
as this should no longer be necessaryexports
field to all packages (#11675)react-router
through different runtime/adapter packages (#11702)crypto
global from the Web Crypto API is now required when using cookie and session APIs
react-router
rather than platform-specific packages: (#11837)
createCookie
createCookieSessionStorage
createMemorySessionStorage
createSessionStorage
installGlobals
function from @remix-run/node
has been updated to define globalThis.crypto
, using Node's require('node:crypto').webcrypto
implementationcreateCookieFactory
createSessionStorageFactory
createCookieSessionStorageFactory
createMemorySessionStorageFactory
@remix-run/router
, @remix-run/server-runtime
, and @remix-run/react
now that they all live in react-router
(#12177)
LoaderFunction
, LoaderFunctionArgs
, ActionFunction
, ActionFunctionArgs
, DataFunctionArgs
, RouteManifest
, LinksFunction
, Route
, EntryRoute
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>
AppData
type in favor of inlining unknown
in the few locations it was usedServerRuntimeMeta*
types in favor of the Meta*
types they were duplicated fromRoute.*
typesRoute.*
typesuseFetcher
previously had an optional generic (used primarily by Remix v2) that expected the data typetypeof loader
/typeof action
)useFetcher<LoaderData>()
useFetcher<typeof loader>()
cookie
dependency to ^1.0.1
- please see the release notes for any breaking changes (#12172)@react-router/cloudflare
- For Remix consumers migrating to React Router, all exports from @remix-run/cloudflare-pages
are now provided for React Router consumers in the @react-router/cloudflare
package. There is no longer a separate package for Cloudflare Pages. (#11801)@react-router/cloudflare
- The @remix-run/cloudflare-workers
package has been deprecated. Remix consumers migrating to React Router should use the @react-router/cloudflare
package directly. For guidance on how to use @react-router/cloudflare
within a Cloudflare Workers context, refer to the Cloudflare Workers template. (#11801)@react-router/dev
- For Remix consumers migrating to React Router, the vitePlugin
and cloudflareDevProxyVitePlugin
exports have been renamed and moved. (#11904)@react-router/dev
- For Remix consumers migrating to React Router who used the Vite plugin's buildEnd
hook, the resolved reactRouterConfig
object no longer contains a publicPath
property since this belongs to Vite, not React Router (#11575)@react-router/dev
- For Remix consumers migrating to React Router, the Vite plugin's manifest
option has been removed (#11573)@react-router/dev
- Update default isbot
version to v5 and drop support for isbot@3
(#11770)
isbot@4
or isbot@5
in your package.json
:
isbot@3
in your package.json
and you have your own entry.server.tsx
file in your repo
isbot@5
independent of the React Router v7 upgradeisbot@3
in your package.json
and you do not have your own entry.server.tsx
file in your repo
isbot@5
in your package.json
@react-router/dev
- For Remix consumers migrating to React Router, Vite manifests (i.e. .vite/manifest.json
) are now written within each build subdirectory, e.g. build/client/.vite/manifest.json
and build/server/.vite/manifest.json
instead of build/.vite/client-manifest.json
and build/.vite/server-manifest.json
. This means that the build output is now much closer to what you'd expect from a typical Vite project. (#11573)
build/.vite
directory to avoid accidentally serving them in production, particularly from the client build. This was later improved with additional logic that deleted these Vite manifest files at the end of the build process unless Vite's build.manifest
had been enabled within the app's Vite config. This greatly reduced the risk of accidentally serving the Vite manifests in production since they're only present when explicitly asked for. As a result, we can now assume that consumers will know that they need to manage these additional files themselves, and React Router can safely generate a more standard Vite build output.react-router
- Params, loader data, and action data as props for route component exports (#11961)react-router
- Add route module type generation (#12019)react-router
- Remove duplicate RouterProvider
implementations (#11679)react-router
- Stabilize unstable_dataStrategy
(#11969)react-router
- Stabilize unstable_patchRoutesOnNavigation
(#11970)react-router
- Add prefetching support to Link
/NavLink
when using Remix SSR (#11402)react-router
- Enhance ScrollRestoration
so it can restore properly on an SSR'd document load (#11401)@react-router/dev
- Add support for the prerender
config in the React Router vite plugin, to support existing SSG use-cases (#11539)@react-router/dev
- Remove internal entry.server.spa.tsx
implementation which was not compatible with the Single Fetch async hydration approach (#11681)@react-router/serve
: Update express.static
configurations to support new prerender
API (#11547)
build/client/assets
folder are served as before, with a 1-year immutable Cache-Control
header.html
and .data
files are not served with a specific Cache-Control
header.data
files are served with Content-Type: text/x-turbo
express.static
, it seems to also add a Cache-Control: public, max-age=0
to .data
filessubstr
with substring
(#12080)react-router
- Fix redirects returned from loaders/actions using data()
(#12021)@react-router/dev
- Enable prerendering for resource routes (#12200)@react-router/dev
- resolve config directory relative to flat output file structure (#12187)react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
Full Changelog: v6.28.0...v7.0.0
Date: 2024-11-06
json
/defer
in favor of returning raw objects
Full Changelog: v6.27.0...v6.28.0
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)unstable_flushSync
option for navigations and fetchers (#11989)unstable_viewTransition
option for navigations and the corresponding unstable_useViewTransitionState
hook (#11989)unstable_dataStrategy
(#11974)unstable_patchRoutesOnNavigation
(#11973)
PatchRoutesOnNavigationFunctionArgs
type for convenience (#11967)?index
param already exists from a prior submission (#12003)useFormAction
bug - when removing ?index
param it would not keep other non-Remix index
params (#12003)preventScrollReset
through redirects during concurrent fetches (#11999)console.error
on fetcher abort due to back-to-back revalidation calls (#12050)partialHydration
when hydrating with errors (#12070)patchRoutesOnNavigation
calls (#12055)
unstable_
APIpatchRoutesOnNavigation
internally so that multiple navigations with the same start/end would only execute the function once and use the same promisepatch
short circuiting if a navigation was interrupted (and the request.signal
aborted) since the first invocation's patch
would no-opimport()
for async routes will already be cached automatically - and if not it's easy enough for users to implement this cache in userlanddiscoveredRoutes
FIFO queue from unstable_patchRoutesOnNavigation
(#11977)
unstable_
APIpatchRoutesOnNavigation
RouteObject
within PatchRoutesOnNavigationFunction
's patch
method so it doesn't expect agnostic route objects passed to patch
(#11967)patchRoutesOnNavigation
directly to useRouteError
instead of wrapping them in a 400 ErrorResponse
instance (#12111)Full Changelog: v6.26.2...v6.27.0
Date: 2024-09-09
unstable_dataStrategy
API to allow for more advanced implementations (#11943)
unstable_dataStrategy
, please review carefully as this includes breaking changes to this APIunstable_HandlerResult
to unstable_DataStrategyResult
unstable_dataStrategy
from a parallel array of unstable_DataStrategyResult[]
(parallel to matches
) to a key/value object of routeId => unstable_DataStrategyResult
match.shouldLoad
)handlerOverride
instead of returning a DataStrategyResult
handlerOverride
will be wrapped up into a DataStrategyResult
and returned fromm match.resolve
match.resolve()
into a final results object you should not need to think about the DataStrategyResult
typehandlerOverride
, then you will need to assign a DataStrategyResult
as the value so React Router knows if it's a successful execution or an error (see examples in the documentation for details)fetcherKey
parameter to unstable_dataStrategy
to allow differentiation from navigational and fetcher callsblocker.proceed
is called quickly/synchronously (#11930)Full Changelog: v6.26.1...v6.26.2
Date: 2024-08-15
unstable_patchRoutesOnMiss
to unstable_patchRoutesOnNavigation
to match new behavior (#11888)unstable_patchRoutesOnNavigation
logic so that we call the method when we match routes with dynamic param or splat segments in case there exists a higher-scoring static route that we've not yet discovered (#11883)
unstable_patchRoutesOnNavigation
against so that we don't re-call on subsequent navigations to the same pathFull Changelog: v6.26.0...v6.26.1
Date: 2024-08-01
replace(url, init?)
alternative to redirect(url, init?)
that performs a history.replaceState
instead of a history.pushState
on client-side navigation redirects (#11811)unstable_data()
API for usage with Remix Single Fetch (#11836)
createStaticHandler.query()
to allow loaders/actions to return arbitrary data along with custom status
/headers
without forcing the serialization of data into a Response
instanceunstable_dataStrategy
such as serializing via turbo-stream
in Remix Single Fetchstatus
field from HandlerResult
status
from unstable_dataStrategy
you should instead do so via unstable_data()
future.v7_partialHydration
along with unstable_patchRoutesOnMiss
(#11838)
router.state.matches
will now include any partial matches so that we can render ancestor HydrateFallback
componentsFull Changelog: v6.25.1...v6.26.0
Date: 2024-07-17
RouterProvider
internals to reduce unnecessary re-renders (#11803)Full Changelog: v6.25.0...v6.25.1
Date: 2024-07-16
v7_skipActionErrorRevalidation
This release stabilizes the future.unstable_skipActionErrorRevalidation
flag into future.v7_skipActionErrorRevalidation
in preparation for the upcoming React Router v7 release.
4xx/5xx
Response
will not trigger a revalidation by defaultshouldRevalidate
's unstable_actionStatus
parameter to actionStatus
future.unstable_skipActionErrorRevalidation
as future.v7_skipActionErrorRevalidation
(#11769)useMatch
so matches/params reflect decoded params (#11789)unstable_patchRoutesOnMiss
(#11786)unstable_patchRoutesOnMiss
that matched a splat route on the server (#11790)Full Changelog: v6.24.1...v6.25.0
Date: 2024-07-03
polyfill.io
reference from warning message because the domain was sold and has since been determined to serve malware (#11741)
NavLinkRenderProps
type for easier typing of custom NavLink
callback (#11553)future.v7_relativeSplatPath
, properly resolve relative paths in splat routes that are children of pathless routes (#11633)router.routes
identity/reflow during route patching (#11740)Full Changelog: v6.24.0...v6.24.1
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]);
}
},
}
);
fetcher.submit
types - remove incorrect navigate
/fetcherKey
/unstable_viewTransition
options because they are only relevant for useSubmit
(#11631)location.state
values passed to <StaticRouter>
(#11495)Full Changelog: v6.23.1...v6.24.0
Date: 2024-05-10
undefined
to be resolved through <Await>
(#11513)document
check when checking for document.startViewTransition
availability (#11544)react-router-dom/server
import back to react-router-dom
instead of index.ts
(#11514)@remix-run/router
- Support unstable_dataStrategy
on staticHandler.queryRoute
(#11515)Full Changelog: v6.23.0...v6.23.1
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.
unstable_dataStrategy
configuration option (#11098, #11377)@remix-run/router
- Add a new future.unstable_skipActionRevalidation
future flag (#11098)@remix-run/router
- SSR: Added a new skipLoaderErrorBubbling
options to the staticHandler.query
method to disable error bubbling by the static handler for use in Remix's Single Fetch implementation (#11098, (#11377))Full Changelog: v6.22.3...v6.23.0
Date: 2024-03-07
future.v7_partialHydration
bug that would re-run loaders below the boundary on hydration if SSR loader errors bubbled to a parent boundary (#11324)future.v7_partialHydration
bug that would consider the router uninitialized if a route did not have a loader (#11325)Full Changelog: v6.22.2...v6.22.3
Date: 2024-02-28
Full Changelog: v6.22.1...v6.22.2
Date: 2024-02-16
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.
window.__reactRouterVersion
for CWV Report detection (#11222)createStaticHandler
future.v7_throwAbortReason
flag to throw request.signal.reason
(defaults to a DOMException
) when a request is aborted instead of an Error
such as new Error("query() call aborted: GET /path")
(#11104)
DOMException
was added in Node v17 so you will not get a DOMException
on Node 16 and below.ErrorResponse
status code if passed to getStaticContextFormError
(#11213)Full Changelog: v6.21.3...v6.22.0
Date: 2024-01-18
NavLink
isPending
when a basename
is used (#11195)unstable_
prefix from Blocker
/BlockerFunction
types (#11187)Full Changelog: v6.21.2...v6.21.3
Date: 2024-01-11
useId
for internal fetcher keys when available (#11166)Full Changelog: v6.21.1...v6.21.2
Date: 2023-12-21
route.lazy
not working correctly on initial SPA load when v7_partialHydration
is specified (#11121)submitting
phase (#11102)resolveTo
(#11097)Full Changelog: v6.21.0...v6.21.1
Date: 2023-12-13
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.
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)
future.v7_relativeSplatPath
flag to implement a breaking bug fix to relative routing when inside a splat route. (#11087)future.v7_partialHydration
future flag that enables partial hydration of a data router when Server-Side Rendering (#11033)ErrorBoundary
's (#11071)loader
/action
functions (#11061)relative="path"
issue when rendering Link
/NavLink
outside of matched routes (#11062)Full Changelog: v6.20.1...v6.21.0
Date: 2023-12-01
useResolvedPath
fix for splat routes due to a large number of applications that were relying on the buggy behavior (see #11052) (#11078)
6.19.0
and 6.20.0
. If you are upgrading from 6.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 of6.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 in6.20.1
and will be re-introducing it behind a future flag in a subsequent release. See #11052 for more details.
PathParam
type from the public API (#10719)v7_fetcherPersist
is enabled (#11044)resolveTo
path resolution in splat routes (#11045)
getPathContributingMatches
UNSAFE_getPathContributingMatches
export from @remix-run/router
since we no longer need this in the react-router
/react-router-dom
layersFull Changelog: v6.19.0...v6.20.0
Date: 2023-11-16
[!WARNING] Please use version
6.20.1
or later instead of6.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 in6.20.1
and will be re-introducing it behind a future flag in a subsequent release. See #11052 for more details.
unstable_flushSync
APIThis 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();
}
unstable_flushSync
option to useNavigate
/useSubmit
/fetcher.load
/fetcher.submit
to opt-out of React.startTransition
and into ReactDOM.flushSync
for state updates (#11005)unstable_
prefix from the useBlocker
hook as it's been in use for enough time that we are confident in the API (#10991)
unstable_usePrompt
due to differences in how browsers handle window.confirm
that prevent React Router from guaranteeing consistent/correct behaviorFix useActionData
so it returns proper contextual action data and not any action data in the tree (#11023)
Fix bug in useResolvedPath
that would cause useResolvedPath(".")
in a splat route to lose the splat portion of the URL path. (#10983)
"."
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 a useFetcher
that remains mounted wasn't getting picked up (#11009)
Fix useFormAction
which was incorrectly inheriting the ?index
query param from child route action
submissions (#11025)
Fix NavLink
active
logic when to
location has a trailing slash (#10734)
Fix types so unstable_usePrompt
can accept a BlockerFunction
in addition to a boolean
(#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.
useFetcher({ key: string })
, which allows you to access the same fetcher instance from different components in your application without prop-drillinguseFetchers
so that they can be looked up by key
Form
and useSubmit
now support optional navigate
/fetcherKey
props/params to allow kicking off a fetcher submission under the hood with an optionally user-specified key
<Form method="post" navigate={false} fetcherKey="my-key">
submit(data, { method: "post", navigate: false, fetcherKey: "my-key" })
useFetchers()
or useFetcher({ key })
to look it up elsewherefuture.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.
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 an idle
stateuseFetchers()
after completion - they served no purpose in there since you can access the data via useFetcher().data
idle
state
useFetchers
while in-flight so you can still access pending/optimistic data after unmountkey
, then it's result will be processed, even if the originating fetcher was unmountedkey
APIs and navigate=false
options (#10960)future.v7_fetcherPersist
flag (#10962)matchPath
(#10768)future
prop on BrowserRouter
, HashRouter
and MemoryRouter
so that it accepts a Partial<FutureConfig>
instead of requiring all flags to be included (#10962)router.getFetcher
/router.deleteFetcher
type definitions which incorrectly specified key
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.
ScrollRestoration
when sessionStorage
is unavailable (#10848)RouterProvider
future
prop type to be a Partial<FutureConfig>
so that not all flags must be specified (#10900)ErrorResponse
type to avoid leaking internal field (#10876)Full Changelog: 6.16.0...6.17.0
Date: 2023-09-13
any
with unknown
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 to any
in React Router and are overridden with unknown
in Remix. In React Router v7 we plan to move these to unknown
as a breaking change. (#10843)
Location
now accepts a generic for the location.state
valueActionFunctionArgs
/ActionFunction
/LoaderFunctionArgs
/LoaderFunction
now accept a generic for the context
parameter (only used in SSR usages via createStaticHandler
)useMatches
(now exported as UIMatch
) accepts generics for match.data
and match.handle
- both of which were already set to unknown
@private
class export ErrorResponse
to an UNSAFE_ErrorResponseImpl
export since it is an implementation detail and there should be no construction of ErrorResponse
instances in userland. This frees us up to export a type ErrorResponse
which correlates to an instance of the class via InstanceType
. Userland code should only ever be using ErrorResponse
as a type and should be type-narrowing via isRouteErrorResponse
. (#10811)ShouldRevalidateFunctionArgs
interface (#10797)_isFetchActionRedirect
, _hasFetcherDoneAnything
) (#10715)query
/queryRoute
calls (#10793)route.lazy
routes (#10778)actionResult
on the arguments object passed to shouldRevalidate
(#10779)Full Changelog: v6.15.0...v6.16.0
Date: 2023-08-10
redirectDocument()
function which allows users to specify that a redirect from a loader
/action
should trigger a document reload (via window.location
) instead of attempting to navigate to the redirected location via React Router (#10705)useRevalidator
is referentially stable across re-renders if revalidations are not actively occurring (#10707)URLSearchParams
and the useSearchParams
hook (#10620)unstable_usePrompt
to avoid throwing an exception if the prompt is unblocked and a navigation is performed synchronously (#10687, #10718)useFormAction()
for unspecified actions since it cannot be determined on the server and causes hydration issues (#10758)queryRoute
that was not always identifying thrown Response
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
<Form state>
prop to populate history.state
on submission navigations (#10630)defer
promise resolves/rejects with undefined
in order to match the behavior of loaders and actions which must return a value or null
(#10690)<ScrollRestoration>
(#10682)Route.lazy
to prohibit returning an empty object (#10634)Error
subclasses such as ReferenceError
/TypeError
(#10633)Full Changelog: v6.14.1...v6.14.2
Date: 2023-06-30
unstable_useBlocker
when used with an unstable blocker function (#10652)@remix-run/router@1.7.1
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"
}
⚠️ 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.
application/json
and text/plain
encodings for useSubmit
/fetcher.submit
. To reflect these additional types, useNavigation
/useFetcher
now also contain navigation.json
/navigation.text
and fetcher.json
/fetcher.text
which include the json/text submission if applicable. (#10413)submitter
element, prefer the built-in new FormData(form, submitter)
instead of the previous manual approach in modern browsers (those that support the new submitter
parameter) (#9865)
type="image"
buttonsformdata-submitter-polyfill
window.history.pushState/replaceState
before updating React Router state (instead of after) so that window.location
matches useLocation
during synchronous React 17 rendering (#10448)
window.location
and should always reference useLocation
when possible, as window.location
will not be in sync 100% of the time (due to popstate
events, concurrent mode, etc.)shouldRevalidate
for fetchers that have not yet completed a data load (#10623)basename
from the location
provided to <ScrollRestoration getKey>
to match the useLocation
behavior (#10550)basename
from locations provided to unstable_useBlocker
functions to match the useLocation
behavior (#10573)unstable_useBlocker
key issues in StrictMode
(#10573)generatePath
when passed a numeric 0
value parameter (#10612)tsc --skipLibCheck:false
issues on React 17 (#10622)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.
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
.
React.startTransition
usage behinds a future flag (#10596)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 of6.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.
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 of6.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.
React.startTransition
supportWith 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.
React.startTransition
(#10438)DOMException
(DataCloneError
) when attempting to perform a PUSH
navigation with non-serializable state. (#10427)jest
and jsdom
(#10453)@remix-run/router@1.6.3
(Changelog)Full Changelog: v6.11.2...v6.12.0
Date: 2023-05-17
basename
duplication in descendant <Routes>
inside a <RouterProvider>
(#10492)SetURLSearchParams
type (#10444)manifest
in _internalSetRoutes
(#10437)Full Changelog: v6.11.1...v6.11.2
Date: 2023-05-03
Component
API within descendant <Routes>
(#10434)useNavigate
from <Routes>
inside a <RouterProvider>
(#10432)<Navigate>
in strict mode when using a data router (#10435)basename
handling when navigating without a path (#10433)/path#hash -> /path#hash
) (#10408)Full Changelog: v6.11.0...v6.11.1
Date: 2023-04-28
basename
support in useFetcher
(#10336)
basename
then you will need to remove the manually prepended basename
from your fetcher
calls (fetcher.load('/basename/route') -> fetcher.load('/route')
)@remix-run/router@1.6.0
(Changelog)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 on useLocation()
(#10336)
BrowserRouter
, these hooks remain unstable across location changes because they still rely on useLocation()
action
submissions or router.revalidate
calls (#10344)Component
instead of element
on a route definition (#10287)<Link to="//">
and other invalid URL values (#10367)useSyncExternalStore
to useState
for internal @remix-run/router
router state syncing in <RouterProvider>
. We found some subtle bugs where router state updates got propagated before other normal useState
updates, which could lead to foot guns in useEffect
calls. (#10377, #10409)<Routes>
when RouterProvider
errors existed (#10374)useNavigate
in the render cycle by setting the activeRef
in a layout effect, allowing the navigate
function to be passed to child components and called in a useEffect
there (#10394)useRevalidator()
to resolve a loader-driven error boundary scenario (#10369)LoaderFunction
/ActionFunction
return type to prevent undefined
from being a valid return value (#10267)fetcher.load
call to a route without a loader
(#10345)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.
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)
future.v7_normalizeFormMethod
is unspecified or set to false
(default v6 behavior),
useNavigation().formMethod
is lowercaseuseFetcher().formMethod
is lowercasefuture.v7_normalizeFormMethod === true
:
useNavigation().formMethod
is UPPERCASEuseFetcher().formMethod
is UPPERCASEcreateStaticHandler
to also check for ErrorBoundary
on routes in addition to errorElement
(#10190)createRoutesFromElements
(#10193)shouldRevalidate
if the fetcher action redirects (#10208)lazy()
errors during router initialization (#10201)instanceof
check for DeferredData
to be resilient to ESM/CJS boundaries in SSR bundling scenarios (#10247)@remix-run/web-fetch@4.3.3
(#10216)Full Changelog: v6.9.0...v6.10.0
Date: 2023-03-10
Component
/ErrorBoundary
route propertiesReact 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.
route.Component
/route.ErrorBoundary
properties (#10045)route.lazy
(#10045)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
<Link to>
as external if they are outside of the router basename
(#10135)basename
(#10076)<Link to>
urls (#10112)StaticRouterProvider
serialized hydration data (#10068)useBlocker
to return IDLE_BLOCKER
during SSR (#10046)defer
loader responses in createStaticHandler
's query()
method (#10077)invariant
to an UNSAFE_invariant
export since it's only intended for internal use (#10066)Full Changelog: v6.8.1...v6.8.2
Date: 2023-02-06
Link
component (now also supports mailto:
urls) (#9994)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 */}
shouldRevalidate
calls (#9948)
shouldRevalidate
function was only being called for explicit revalidation scenarios (after a mutation, manual useRevalidator
call, or an X-Remix-Revalidate
header used for cookie setting in Remix). It was not properly being called on implicit revalidation scenarios that also apply to navigation loader
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.current*
/next*
parameters reflected the static fetcher.load
URL (and thus were identical). Instead, they should have reflected the navigation that triggered the revalidation (as the form*
parameters did). These parameters now correctly reflect the triggering navigation.useSearchParams
(#9969)preventScrollReset
on <fetcher.Form>
(#9963)pagehide
instead of beforeunload
for <ScrollRestoration>
. This has better cross-browser support, specifically on Mobile Safari. (#9945)instanceof
check from isRouteErrorResponse
to avoid bundling issues on the server (#9930)defer
call only contains critical data and remove the AbortController
(#9965)File
FormData
entries (#9867)react-router-dom-v5-compat
- Fix SSR useLayoutEffect
console.error
when using CompatRouter
(#9820)Full Changelog: v6.7.0...v6.8.0
Date: 2023-01-18
unstable_useBlocker
/unstable_usePrompt
hooks for blocking navigations within the app's location origin (#9709, #9932)preventScrollReset
prop to <Form>
(#9886)useBeforeUnload
(#9709)generatePath
when optional params are present (#9764)<Await>
to accept ReactNode
as children function return result (#9896)jsdom
bug workaround in tests (#9824)Full Changelog: v6.6.2...v6.7.0
Date: 2023-01-09
useId
consistency during SSR (#9805)Full Changelog: v6.6.1...v6.6.2
Date: 2022-12-23
shouldRevalidate
on action redirects (#9777, #9782)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.
unstable_
prefix from createStaticHandler
/createStaticRouter
/StaticRouterProvider
(#9738)useBeforeUnload()
hook (#9664)<Form method>
and useSubmit
method values (#9664)<button formmethod>
form submission overriddes (#9664)replace
on submissions and PUSH
on submission to new paths (#9734)useLoaderData
usage in errorElement
(#9735)Error
objects from StaticRouterProvider
(#9664)hydrationData
(#9664)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
<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 the useParams
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"
...
}
headers
on loader
request
's after SSR document action
request (#9721)Full Changelog: v6.4.5...v6.5.0
Date: 2022-12-07
GET
request (#9680)instanceof Response
checks in favor of isResponse
(#9690)URL
creation in Cloudflare Pages or other non-browser-environments (#9682, #9689)requestContext
support to static handler query
/queryRoute
(#9696)
queryRoute(path, routeId)
has been changed to queryRoute(path, { routeId, requestContext })
Full Changelog: v6.4.4...v6.4.5
Date: 2022-11-30
action
/loader
function returns undefined
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 your loader
/action
returns a value, and you may return null
if you don't wish to return anything. (#9511)basename
in static data routers (#9591)ErrorResponse
bodies to contain more descriptive text in internal 403/404/405 scenariosNavLink
and descendant <Routes>
(#9589, #9647)ErrorResponse
instances when using built-in hydration (#9593)basename
in static data routers (#9591)@remix-run/router@1.0.4
react-router@6.4.4
Full Changelog: v6.4.3...v6.4.4
Date: 2022-11-01
<a href>
values when using createHashRouter
(#9409)formAction
pathnames when an index
route also has a path
(#9486)relative=path
prop on NavLink
(#9453)NavLink
behavior for root urls (#9497)useRoutes
should be able to return null
when passing locationArg
(#9485)initialEntries
type in createMemoryRouter
(#9498)basename
and relative routing in loader
/action
redirects (#9447)action
function (#9455)@remix-run/router
(#9446)createURL
in local file execution in Firefox (#9464)Full Changelog: v6.4.2...v6.4.3
Date: 2022-10-06
basename
in useFormAction
(#9352)IndexRouteObject
and NonIndexRouteObject
types to make hasErrorElement
optional (#9394)RouteObject
/RouteProps
types to surface the error in TypeScript. (#9366)Full Changelog: v6.4.1...v6.4.2
Date: 2022-09-22
initialEntries
(#9288)?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
createMemoryRouter
<RouterProvider>
loader
and mutate with a Route action
errorElement
defer
and Await
New react-router-dom
APIs
createBrowserRouter
/createHashRouter
<Form>
componentuseFetcher()
defer
and Await
<ScrollRestoration>
<Link relative="path">
(#9160)useLocation
returns the scoped location inside a <Routes location>
component (#9094)<Link replace>
prop if it is defined (#8779)Full Changelog: v6.3.0...v6.4.0
Date: 2022-03-31
Full Changelog: v6.2.2...v6.3.0
Date: 2022-02-28
Full Changelog: v6.2.1...v6.2.2
Date: 2021-12-17
history
dependency to 5.2.0
.Full Changelog: v6.2.0...v6.2.1
Date: 2021-12-17
RouteProps
element
type, which should be a ReactNode
(#8473)useOutlet
for top-level routes (#8483)Full Changelog: v6.1.1...v6.2.0
Date: 2021-12-11
HistoryRouter
as unstable_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 a context
prop. This value is passed to child routes and is accessible via the new useOutletContext
hook. See the API docs for details. (#8461)<NavLink>
can now receive a child function for access to its props. (#8164)useMatch
and matchPath
. For example, when you call useMatch("foo/:bar/:baz")
, the path is parsed and the return type will be PathMatch<"bar" | "baz">
. (#8030)Full Changelog: v6.0.2...v6.1.0
Date: 2021-11-09
reloadDocument
prop to <Link>
. This allows <Link>
to function like a normal anchor tag by reloading the document after navigation while maintaining the relative to
resolution (#8283)Full Changelog: v6.0.1...v6.0.2
Date: 2021-11-05
<StaticRouter location>
value (#8243)<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.