NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #1611 most downloaded on npm
Declarative routing for React
Last release 6 days ago
15 Sep 2026
Ships on a steady schedule
a new release about every 2 weeks
Nearly every release is documented
notes for 60 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
13 years old
1186 releases · first in 2014
React Router now supports an alternative way to define your route element and errorElement fields as React Components instead of React Elements. You c
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". (#10045)
Example JSON Syntax
// Both of these work the same:
const elementRoutes = [{
path: '/',
element: <Home />,
errorElement: <HomeError />,
}]
const componentRoutes = [{
path: '/',
Component: Home,
ErrorBoundary: HomeError,
}]
function Home() { ... }
function HomeError() { ... }
Example JSX Syntax
// Both of these work the same:
const elementRoutes = createRoutesFromElements(
<Route path='/' element={<Home />} errorElement={<HomeError /> } />
);
const componentRoutes = createRoutesFromElements(
<Route path='/' Component={Home} ErrorBoundary={HomeError} />
);
function Home() { ... }
function HomeError() { ... }
Introducing Lazy Route Modules! (#10045)
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.
generatePath incorrectly applying parameters in some cases (#10078)[react-router-dom-v5-compat] Add missed data router API re-exports (#10171)Full Changelog: https://github.com/remix-run/react-router/compare/react-router@6.8.2...react-router@6.9.0
One column per quarter.
Nothing published for this version
Treat same-origin absolute URLs in as external if they are outside of the router basename
<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: https://github.com/remix-run/react-router/compare/react-router@6.8.1...react-router@6.8.2
@remix-run/router@1.3.3Date: 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
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Remove inaccurate console warning for POP navigations and update active blocker logic
Link component (now also supports mailto: urls) (#9994)Full Changelog: https://github.com/remix-run/react-router/compare/react-router@6.8.0...react-router@6.8.1
@remix-run/router@1.3.2Date: 2023-02-06
Link component (now also supports mailto: urls) (#9994)Full Changelog: v6.8.0...v6.8.1
Nothing published for this version
Support absolute URLs in . 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
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 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
@remix-run/router@1.3.1Nothing published for this version
Nothing published for this version
Add unstable_useBlocker/unstable_usePrompt hooks for blocking navigations within the app's location origin (#9709, #9932)
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
unstable_useBlocker hook for blocking navigations within the app's location origin (#9709)Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Ensure useId consistency during SSR
useId consistency during SSR (#9805)Full Changelog: https://github.com/remix-run/react-router/compare/react-router@6.6.1...react-router@6.6.2
useId consistency during SSR (#9805)Date: 2023-01-09
useId consistency during SSR (#9805)Full Changelog: v6.6.1...v6.6.2
Nothing published for this version
Include submission info in shouldRevalidate on action redirects (#9777, #9782)
@remix-run/router@1.2.1Date: 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
Nothing published for this version
Nothing published for this version
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 Reac
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: https://github.com/remix-run/react-router/compare/react-router@6.5.0...react-router@6.6.0
useLoaderData usage in errorElement (#9735)@remix-run/router@1.2.0Nothing published for this version
This release introduces support for Optional Route Segments. Now, adding a ? to the end of any path segment will make that entire segment optional. Th
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/:widget3Optional 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: https://github.com/remix-run/react-router/compare/react-router@6.4.5...react-router@6.5.0
Nothing published for this version
Nothing published for this version
Fix requests sent to revalidating loaders so they reflect a GET request
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: https://github.com/remix-run/react-router/compare/react-router@6.4.4...react-router@6.4.5
@remix-run/router@1.0.5Date: 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
Nothing published for this version
Nothing published for this version
Nothing published for this version
### Patch Changes - Updated dependencies: - @remix-run/router@1.0.4
@remix-run/router@1.0.4Date: 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.4react-router@6.4.4Full Changelog: v6.4.3...v6.4.4
Nothing published for this version
Nothing published for this version
Nothing published for this version
Generate correct values when using createHashRouter
<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: https://github.com/remix-run/react-router/compare/react-router@6.4.2...react-router@6.4.3
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
Nothing published for this version
Nothing published for this version
Nothing published for this version
Respect basename in useFormAction
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: https://github.com/remix-run/react-router/compare/react-router@6.4.1...react-router@6.4.2
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
Nothing published for this version
Nothing published for this version
Preserve state from initialEntries
initialEntries (#9288)@remix-run/router@1.0.1Date: 2022-09-22
initialEntries (#9288)?index for fetcher get submissions to index routes (#9312)Full Changelog: v6.4.0...v6.4.1
Nothing published for this version
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
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.
createMemoryRouter<RouterProvider>loader and mutate with a Route actionerrorElementdefer and Awaitreact-router-dom APIscreateBrowserRouter/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)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 actionerrorElementdefer and AwaitNew 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
Nothing published for this version
fix: rename resetScroll -> preventScrollReset
Nothing published for this version
Nothing published for this version
We are no longer replacing the Promise on loaderData with the value/error when it settles so it's now always a Promise.
c3406eb9: fix: Rename <Deferred> to <Await> (#9095)
Promise on loaderData with the value/error
when it settles so it's now always a Promise.<Deferred value={promise}> to
<Await resolve={promise}> for clarity, and it also now supports using
<Await> with raw promises from anywhere, not only those on loaderData
from a defer() call.
useAsyncValue/useAsyncErrorUpdated dependencies
Removes from inside , requires users to render their own suspense boundaries
feat: Deferred API Updates (#9070)
<Suspense> from inside <Deferred>, requires users to render their own suspense boundariesDeferred to use a true error boundary to catch render errors as well as data errorsreturn deferred([ await critical(), lazy() ])return deferred(lazy())Deferrable/ResolvedDeferrable in favor of raw Promise's and AwaiteduseDeferredData until useLoaderData generic is decided in 6.5Updated dependencies
Feat: adds deferred support to data routers
Feat: adds deferred support to data routers (#9002)
Returning a deferred from a loader allows you to separate critical loader data that you want to wait for prior to rendering the destination page from non-critical data that you are OK to show a spinner for until it loads.
// In your route loader, return a deferred() and choose per-key whether to
// await the promise or not. As soon as the awaited promises resolve, the
// page will be rendered.
function loader() {
return deferred({
critical: await getCriticalData(),
lazy: getLazyData(),
});
};
// In your route element, grab the values from useLoaderData and render them
// with <Deferred>
function DeferredPage() {
let data = useLoaderData();
return (
<>
<p>Critical Data: {data.critical}</p>
<Deferred
value={data.lazy}
fallback={<p>Loading...</p>}
errorElement={<RenderDeferredError />}>
<RenderDeferredData />
</Deferred>
</>
);
}
// Use separate components to render the data once it resolves, and access it
// via the useDeferredData hook
function RenderDeferredData() {
let data = useDeferredData();
return <p>Lazy: {data}</p>;
}
function RenderDeferredError() {
let data = useRouteError();
return <p>Error! {data.message} {data.stack}</p>;
}
If you want to skip the separate components, you can use the Render Props pattern and handle the rendering of the deferred data inline:
function DeferredPage() {
let data = useLoaderData();
return (
<>
<p>Critical Data: {data.critical}</p>
<Deferred value={data.lazy} fallback={<p>Loading...</p>}>
{(data) => <p>{data}</p>}
</Deferred>
</>
);
}
feat: add basename support for data routers (#9026)
fix: Fix trailing slash behavior on pathless routing when using a basename (#9045)
Updated dependencies
fix: Make path resolution trailing slash agnostic
Add support for functional updates in useSearchParams (similar to the useState callback signature)
useSearchParams (similar to the useState callback signature) (#8955)@remix-run/router@0.2.0-pre.2Nothing published for this version
Fix broken require for CJS builds
031d5d1f: Fix missing dist files
Make fallbackElement optional and change type to ReactNode (type changes only)
fallbackElement optional and change type to ReactNode (type changes only) (#8896)Your coding agent can read these notes before it upgrades. Set up the MCP server →