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
- 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.1
- v6.0.0
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_relativeSplatPathflag to implement a breaking bug fix to relative routing when inside a splat route. (#11087) - Add a new
future.v7_partialHydrationfuture flag that enables partial hydration of a data router when Server-Side Rendering (#11033)
- Properly handle falsy error values in
ErrorBoundary's (#11071) - Catch and bubble errors thrown when trying to unwrap responses from
loader/actionfunctions (#11061) - Fix
relative="path"issue when renderingLink/NavLinkoutside of matched routes (#11062)
Full Changelog: v6.20.1...v6.21.0
- Revert the
useResolvedPathfix for splat routes due to a large number of applications that were relying on the buggy behavior (see #11052) (#11078)- We plan to re-introduce this fix behind a future flag in the next minor version (see this comment)
- This fix was included in versions
6.19.0and6.20.0. If you are upgrading from6.18.0or earlier, you would not have been impacted by this fix.
Full Changelog: v6.20.0...v6.20.1
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
PathParamtype from the public API (#10719)
- Do not revalidate unmounted fetchers when
v7_fetcherPersistis enabled (#11044) - Fix bug with
resolveTopath resolution in splat routes (#11045)- This is a follow up to #10983 to handle the few other code paths using
getPathContributingMatches - This removes the
UNSAFE_getPathContributingMatchesexport from@remix-run/routersince we no longer need this in thereact-router/react-router-domlayers
- This is a follow up to #10983 to handle the few other code paths using
Full Changelog: v6.19.0...v6.20.0
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_flushSyncoption touseNavigate/useSubmit/fetcher.load/fetcher.submitto opt-out ofReact.startTransitionand intoReactDOM.flushSyncfor state updates (#11005) - Remove the
unstable_prefix from theuseBlockerhook as it's been in use for enough time that we are confident in the API (#10991)- We do not plan to remove the prefix from
unstable_usePromptdue to differences in how browsers handlewindow.confirmthat prevent React Router from guaranteeing consistent/correct behavior
- We do not plan to remove the prefix from
-
Fix
useActionDataso it returns proper contextual action data and not any action data in the tree (#11023) -
Fix bug in
useResolvedPaththat would causeuseResolvedPath(".")in a splat route to lose the splat portion of the URL path. (#10983)⚠️ This fixes a quite long-standing bug specifically for"."paths inside a splat route which incorrectly dropped the splat portion of the URL. If you are relative routing via"."inside a splat route in your application you should double check that your logic is not relying on this buggy behavior and update accordingly.
-
Fix issue where a changing fetcher
keyin auseFetcherthat remains mounted wasn't getting picked up (#11009) -
Fix
useFormActionwhich was incorrectly inheriting the?indexquery param from child routeactionsubmissions (#11025) -
Fix
NavLinkactivelogic whentolocation has a trailing slash (#10734) -
Fix types so
unstable_usePromptcan accept aBlockerFunctionin addition to aboolean(#10991) -
Fix
relative="path"bug where relative path calculations started from the full location pathname, instead of from the current contextual route pathname. (#11006)<Route path="/a"> <Route path="/b" element={<Component />}> <Route path="/c" /> </Route> </Route>; function Component() { return ( <> {/* This is now correctly relative to /a/b, not /a/b/c */} <Link to=".." relative="path" /> <Outlet /> </> ); }
Full Changelog: 6.18.0...6.19.0
Per this RFC, we've introduced some new APIs that give you more granular control over your fetcher behaviors.
- You may now specify your own fetcher identifier via
useFetcher({ key: string }), which allows you to access the same fetcher instance from different components in your application without prop-drilling - Fetcher keys are now exposed on the fetchers returned from
useFetchersso that they can be looked up bykey FormanduseSumbitnow support optionalnavigate/fetcherKeyprops/params to allow kicking off a fetcher submission under the hood with an optionally user-specifiedkey<Form method="post" navigate={false} fetcherKey="my-key">submit(data, { method: "post", navigate: false, fetcherKey: "my-key" })- Invoking a fetcher in this way is ephemeral and stateless
- If you need to access the state of one of these fetchers, you will need to leverage
useFetchers()oruseFetcher({ key })to look it up elsewhere
Per the same RFC as above, we've introduced a new future.v7_fetcherPersist flag that allows you to opt-into the new fetcher persistence/cleanup behavior. Instead of being immediately cleaned up on unmount, fetchers will persist until they return to an idle state. This makes pending/optimistic UI much easier in scenarios where the originating fetcher needs to unmount.
- This is sort of a long-standing bug fix as the
useFetchers()API was always supposed to only reflect in-flight fetcher information for pending/optimistic UI -- it was not intended to reflect fetcher data or hang onto fetchers after they returned to anidlestate - Keep an eye out for the following specific behavioral changes when opting into this flag and check your app for compatibility:
- Fetchers that complete while still mounted will no longer appear in
useFetchers()after completion - they served no purpose in there since you can access the data viauseFetcher().data - Fetchers that previously unmounted while in-flight will not be immediately aborted and will instead be cleaned up once they return to an
idlestate- They will remain exposed via
useFetcherswhile in-flight so you can still access pending/optimistic data after unmount - If a fetcher is no longer mounted when it completes, then it's result will not be post processed - e.g., redirects will not be followed and errors will not bubble up in the UI
- However, if a fetcher was re-mounted elsewhere in the tree using the same
key, then it's result will be processed, even if the originating fetcher was unmounted
- They will remain exposed via
- Fetchers that complete while still mounted will no longer appear in
- Add fetcher
keyAPIs andnavigate=falseoptions (#10960) - Add
future.v7_fetcherPersistflag (#10962) - Add support for optional path segments in
matchPath(#10768)
- Fix the
futureprop onBrowserRouter,HashRouterandMemoryRouterso that it accepts aPartial<FutureConfig>instead of requiring all flags to be included (#10962) - Fix
router.getFetcher/router.deleteFetchertype definitions which incorrectly specifiedkeyas an optional parameter (#10960)
Full Changelog: 6.17.0...6.18.0
We're excited to release experimental support for the 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
ScrollRestorationwhensessionStorageis unavailable (#10848) - Fix
RouterProviderfutureprop type to be aPartial<FutureConfig>so that not all flags must be specified (#10900) - Allow 404 detection to leverage root route error boundary if path contains a URL segment (#10852)
- Fix
ErrorResponsetype to avoid leaking internal field (#10876)
Full Changelog: 6.16.0...6.17.0
- In order to move towards stricter TypeScript support in the future, we're aiming to replace current usages of
anywithunknownon exposed typings for user-provided data. To do this in Remix v2 without introducing breaking changes in React Router v6, we have added generics to a number of shared types. These continue to default toanyin React Router and are overridden withunknownin Remix. In React Router v7 we plan to move these tounknownas a breaking change. (#10843)Locationnow accepts a generic for thelocation.statevalueActionFunctionArgs/ActionFunction/LoaderFunctionArgs/LoaderFunctionnow accept a generic for thecontextparameter (only used in SSR usages viacreateStaticHandler)- The return type of
useMatches(now exported asUIMatch) accepts generics formatch.dataandmatch.handle- both of which were already set tounknown
- Move the
@privateclass exportErrorResponseto anUNSAFE_ErrorResponseImplexport since it is an implementation detail and there should be no construction ofErrorResponseinstances in userland. This frees us up to export atype ErrorResponsewhich correlates to an instance of the class viaInstanceType. Userland code should only ever be usingErrorResponseas a type and should be type-narrowing viaisRouteErrorResponse. (#10811) - Export
ShouldRevalidateFunctionArgsinterface (#10797) - Removed private/internal APIs only required for the Remix v1 backwards compatibility layer and no longer needed in Remix v2 (
_isFetchActionRedirect,_hasFetcherDoneAnything) (#10715)
- Properly encode rendered URIs in server rendering to avoid hydration errors (#10769)
- Add method/url to error message on aborted
query/queryRoutecalls (#10793) - Fix a race-condition with loader/action-thrown errors on
route.lazyroutes (#10778) - Fix type for
actionResulton the arguments object passed toshouldRevalidate(#10779)
Full Changelog: v6.15.0...v6.16.0
- Add's a new
redirectDocument()function which allows users to specify that a redirect from aloader/actionshould trigger a document reload (viawindow.location) instead of attempting to navigate to the redirected location via React Router (#10705)
- Ensure
useRevalidatoris referentially stable across re-renders if revalidations are not actively occurring (#10707) - Ensure hash history always includes a leading slash on hash pathnames (#10753)
- Fixes an edge-case affecting web extensions in Firefox that use
URLSearchParamsand theuseSearchParamshook (#10620) - Reorder effects in
unstable_usePromptto avoid throwing an exception if the prompt is unblocked and a navigation is performed synchronously (#10687, #10718) - SSR: Do not include hash in
useFormAction()for unspecified actions since it cannot be determined on the server and causes hydration issues (#10758) - SSR: Fix an issue in
queryRoutethat was not always identifying thrownResponseinstances (#10717) react-router-native: Update@ungap/url-search-paramsdependency from^0.1.4to^0.2.2(#10590)
Full Changelog: v6.14.2...v6.15.0
- Add missing
<Form state>prop to populatehistory.stateon submission navigations (#10630) - Trigger an error if a
deferpromise resolves/rejects withundefinedin order to match the behavior of loaders and actions which must return a value ornull(#10690) - Properly handle fetcher redirects interrupted by normal navigations (#10674)
- Initial-load fetchers should not automatically revalidate on GET navigations (#10688)
- Properly decode element id when emulating hash scrolling via
<ScrollRestoration>(#10682) - Typescript: Enhance the return type of
Route.lazyto prohibit returning an empty object (#10634) - SSR: Support proper hydration of
Errorsubclasses such asReferenceError/TypeError(#10633)
Full Changelog: v6.14.1...v6.14.2
- Fix loop in
unstable_useBlockerwhen used with an unstable blocker function (#10652) - Fix issues with reused blockers on subsequent navigations (#10656)
- Updated dependencies:
@remix-run/router@1.7.1
Full Changelog: v6.14.0...v6.14.1
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/jsonandtext/plainencodings foruseSubmit/fetcher.submit. To reflect these additional types,useNavigation/useFetchernow also containnavigation.json/navigation.textandfetcher.json/fetcher.textwhich include the json/text submission if applicable. (#10413)
- When submitting a form from a
submitterelement, prefer the built-innew FormData(form, submitter)instead of the previous manual approach in modern browsers (those that support the newsubmitterparameter) (#9865)- For browsers that don't support it, we continue to just append the submit button's entry to the end, and we also add rudimentary support for
type="image"buttons - If developers want full spec-compliant support for legacy browsers, they can use the
formdata-submitter-polyfill
- For browsers that don't support it, we continue to just append the submit button's entry to the end, and we also add rudimentary support for
- Call
window.history.pushState/replaceStatebefore updating React Router state (instead of after) so thatwindow.locationmatchesuseLocationduring synchronous React 17 rendering (#10448)⚠️ Note: generally apps should not be relying onwindow.locationand should always referenceuseLocationwhen possible, aswindow.locationwill not be in sync 100% of the time (due topopstateevents, concurrent mode, etc.)
- Avoid calling
shouldRevalidatefor fetchers that have not yet completed a data load (#10623) - Strip
basenamefrom thelocationprovided to<ScrollRestoration getKey>to match theuseLocationbehavior (#10550) - Strip
basenamefrom locations provided tounstable_useBlockerfunctions to match theuseLocationbehavior (#10573) - Fix
unstable_useBlockerkey issues inStrictMode(#10573) - Fix
generatePathwhen passed a numeric0value parameter (#10612) - Fix
tsc --skipLibCheck:falseissues on React 17 (#10622) - Upgrade
typescriptto 5.1 (#10581)
Full Changelog: v6.13.0...v6.14.0
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.startTransitionusage behinds a future flag (#10596)
- Work around webpack/terser
React.startTransitionminification bug in production mode (#10588)
Full Changelog: v6.12.1...v6.13.0
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.startTransitionto fix webpack + react 17 compilation error (#10569)
Full Changelog: v6.12.0...v6.12.1
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 aPUSHnavigation with non-serializable state. (#10427) - Ensure revalidations happen when hash is present (#10516)
- Upgrade
jestandjsdom(#10453) - Updated dependencies:
@remix-run/router@1.6.3(Changelog)
Full Changelog: v6.11.2...v6.12.0
- Fix
basenameduplication in descendant<Routes>inside a<RouterProvider>(#10492) - Fix bug where initial data load would not kick off when hash is present (#10493)
- Export
SetURLSearchParamstype (#10444) - Fix Remix HMR-driven error boundaries by properly reconstructing new routes and
manifestin_internalSetRoutes(#10437)
Full Changelog: v6.11.1...v6.11.2
- Fix usage of
ComponentAPI within descendant<Routes>(#10434) - Fix bug when calling
useNavigatefrom<Routes>inside a<RouterProvider>(#10432) - Fix usage of
<Navigate>in strict mode when using a data router (#10435) - Fix
basenamehandling when navigating without a path (#10433) - "Same hash" navigations no longer re-run loaders to match browser behavior (i.e.
/path#hash -> /path#hash) (#10408)
Full Changelog: v6.11.0...v6.11.1
- Enable
basenamesupport inuseFetcher(#10336)- If you were previously working around this issue by manually prepending the
basenamethen you will need to remove the manually prependedbasenamefrom yourfetchercalls (fetcher.load('/basename/route') -> fetcher.load('/route'))
- If you were previously working around this issue by manually prepending the
- Updated dependencies:
@remix-run/router@1.6.0(Changelog)
- When using a
RouterProvider,useNavigate/useSubmit/fetcher.submitare now stable across location changes, since we can handle relative routing via the@remix-run/routerinstance and get rid of our dependence onuseLocation()(#10336)- When using
BrowserRouter, these hooks remain unstable across location changes because they still rely onuseLocation()
- When using
- Fetchers should no longer revalidate on search params changes or routing to the same URL, and will only revalidate on
actionsubmissions orrouter.revalidatecalls (#10344) - Fix inadvertent re-renders when using
Componentinstead ofelementon a route definition (#10287) - Fail gracefully on
<Link to="//">and other invalid URL values (#10367) - Switched from
useSyncExternalStoretouseStatefor internal@remix-run/routerrouter state syncing in<RouterProvider>. We found some subtle bugs where router state updates got propagated before other normaluseStateupdates, which could lead to foot guns inuseEffectcalls. (#10377, #10409) - Log loader/action errors caught by the default error boundary to the console in dev for easier stack trace evaluation (#10286)
- Fix bug preventing rendering of descendant
<Routes>whenRouterProvidererrors existed (#10374) - Fix detection of
useNavigatein the render cycle by setting theactiveRefin a layout effect, allowing thenavigatefunction to be passed to child components and called in auseEffectthere (#10394) - Allow
useRevalidator()to resolve a loader-driven error boundary scenario (#10369) - Enhance
LoaderFunction/ActionFunctionreturn type to preventundefinedfrom being a valid return value (#10267) - Ensure proper 404 error on
fetcher.loadcall to a route without aloader(#10345) - Decouple
AbortControllerusage between revalidating fetchers and the thing that triggered them such that the unmount/deletion of a revalidating fetcher doesn't impact the ongoing triggering navigation/revalidation (#10271)
Full Changelog: v6.10.0...v6.11.0
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_normalizeFormMethodwhich will normalize the exposeduseNavigation()/useFetcher()formMethodfields as uppercase HTTP methods to align with thefetch()(and some Remix) behavior. (#10207)- When
future.v7_normalizeFormMethodis unspecified or set tofalse(default v6 behavior),useNavigation().formMethodis lowercaseuseFetcher().formMethodis lowercase
- When
future.v7_normalizeFormMethod === true:useNavigation().formMethodis UPPERCASEuseFetcher().formMethodis UPPERCASE
- When
- Fix
createStaticHandlerto also check forErrorBoundaryon routes in addition toerrorElement(#10190) - Fix route ID generation when using Fragments in
createRoutesFromElements(#10193) - Provide fetcher submission to
shouldRevalidateif the fetcher action redirects (#10208) - Properly handle
lazy()errors during router initialization (#10201) - Remove
instanceofcheck forDeferredDatato be resilient to ESM/CJS boundaries in SSR bundling scenarios (#10247) - Update to latest
@remix-run/web-fetch@4.3.3(#10216)
Full Changelog: v6.9.0...v6.10.0
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.ErrorBoundaryproperties (#10045) - Add support for
route.lazy(#10045)
- Improve memoization for context providers to avoid unnecessary re-renders (#9983)
- Fix
generatePathincorrectly applying parameters in some cases (#10078) [react-router-dom-v5-compat]Add missed data router API re-exports (#10171)
Full Changelog: v6.8.2...v6.9.0
- Treat same-origin absolute URLs in
<Link to>as external if they are outside of the routerbasename(#10135) - Correctly perform a hard redirect for same-origin absolute URLs outside of the router
basename(#10076) - Fix SSR of absolute
<Link to>urls (#10112) - Properly escape HTML characters in
StaticRouterProviderserialized hydration data (#10068) - Fix
useBlockerto returnIDLE_BLOCKERduring SSR (#10046) - Ensure status code and headers are maintained for
deferloader responses increateStaticHandler'squery()method (#10077) - Change
invariantto anUNSAFE_invariantexport since it's only intended for internal use (#10066)
Full Changelog: v6.8.1...v6.8.2
- Remove inaccurate console warning for POP navigations and update active blocker logic (#10030)
- Only check for differing origin on absolute URL redirects (#10033)
- Improved absolute url detection in
Linkcomponent (now also supportsmailto:urls) (#9994) - Fix partial object (search or hash only) pathnames losing current path value (#10029)
Full Changelog: v6.8.0...v6.8.1
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
shouldRevalidatecalls (#9948)- The
shouldRevalidatefunction was only being called for explicit revalidation scenarios (after a mutation, manualuseRevalidatorcall, or anX-Remix-Revalidateheader used for cookie setting in Remix). It was not properly being called on implicit revalidation scenarios that also apply to navigationloaderrevalidation, such as a change in search params or clicking a link for the page we're already on. It's now correctly called in those additional scenarios. - The parameters being passed were incorrect and inconsistent with one another since the
current*/next*parameters reflected the staticfetcher.loadURL (and thus were identical). Instead, they should have reflected the the navigation that triggered the revalidation (as theform*parameters did). These parameters now correctly reflect the triggering navigation.
- The
- Fix bug with search params removal via
useSearchParams(#9969) - Respect
preventScrollReseton<fetcher.Form>(#9963) - Fix navigation for hash routers on manual URL changes (#9980)
- Use
pagehideinstead ofbeforeunloadfor<ScrollRestoration>. This has better cross-browser support, specifically on Mobile Safari. (#9945) - Do not short circuit on hash change only mutation submissions (#9944)
- Remove
instanceofcheck fromisRouteErrorResponseto avoid bundling issues on the server (#9930) - Detect when a
defercall only contains critical data and remove theAbortController(#9965) - Send the name as the value when url-encoding
FileFormDataentries (#9867) react-router-dom-v5-compat- Fix SSRuseLayoutEffectconsole.errorwhen usingCompatRouter(#9820)
Full Changelog: v6.7.0...v6.8.0
- Add
unstable_useBlocker/unstable_usePrompthooks for blocking navigations within the app's location origin (#9709, #9932) - Add
preventScrollResetprop to<Form>(#9886)
- Added pass-through event listener options argument to
useBeforeUnload(#9709) - Fix
generatePathwhen optional params are present (#9764) - Update
<Await>to acceptReactNodeas children function return result (#9896) - Improved absolute redirect url detection in actions/loaders (#9829)
- Fix URL creation with memory histories (#9814)
- Fix scroll reset if a submission redirects (#9886)
- Fix 404 bug with same-origin absolute redirects (#9913)
- Streamline
jsdombug workaround in tests (#9824)
Full Changelog: v6.6.2...v6.7.0
- Ensure
useIdconsistency during SSR (#9805)
Full Changelog: v6.6.1...v6.6.2
- Include submission info in
shouldRevalidateon action redirects (#9777, #9782) - Reset
actionDataon action redirect to current location (#9772)
Full Changelog: v6.6.0...v6.6.1
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>anduseSubmitmethod values (#9664) - Fix
<button formmethod>form submission overriddes (#9664) - Fix explicit
replaceon submissions andPUSHon submission to new paths (#9734) - Prevent
useLoaderDatausage inerrorElement(#9735) - Proper hydration of
Errorobjects fromStaticRouterProvider(#9664) - Skip initial scroll restoration for SSR apps with
hydrationData(#9664) - Fix a few bugs where loader/action data wasn't properly cleared on errors (#9735)
Full Changelog: v6.5.0...v6.6.0
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 theuseParamscall site: (#9506)
// Old behavior at URL /prefix-123
<Route path="prefix-:id" element={<Comp /> }>
function Comp() {
let params = useParams(); // { id: '123' }
let id = params.id; // "123"
...
}
// New behavior at URL /prefix-123
<Route path=":id" element={<Comp /> }>
function Comp() {
let params = useParams(); // { id: 'prefix-123' }
let id = params.id.replace(/^prefix-/, ''); // "123"
...
}- Persist
headersonloaderrequest's after SSR documentactionrequest (#9721) - Fix requests sent to revalidating loaders so they reflect a GET request (#9660)
- Fix issue with deeply nested optional segments (#9727)
- GET forms now expose a submission on the loading navigation (#9695)
- Fix error boundary tracking for multiple errors bubbling to the same boundary (#9702)
Full Changelog: v6.4.5...v6.5.0
- Fix requests sent to revalidating loaders so they reflect a
GETrequest (#9680) - Remove
instanceof Responsechecks in favor ofisResponse(#9690) - Fix
URLcreation in Cloudflare Pages or other non-browser-environments (#9682, #9689) - Add
requestContextsupport to static handlerquery/queryRoute(#9696)- Note that the unstable API of
queryRoute(path, routeId)has been changed toqueryRoute(path, { routeId, requestContext })
- Note that the unstable API of
Full Changelog: v6.4.4...v6.4.5
- Throw an error if an
action/loaderfunction returnsundefinedas revalidations need to know whether the loader has previously been executed.undefinedalso causes issues during SSR stringification for hydration. You should always ensure yourloader/actionreturns a value, and you may returnnullif you don't wish to return anything. (#9511) - Properly handle redirects to external domains (#9590, #9654)
- Preserve the HTTP method on 307/308 redirects (#9597)
- Support
basenamein static data routers (#9591) - Enhanced
ErrorResponsebodies to contain more descriptive text in internal 403/404/405 scenarios - Fix issues with encoded characters in
NavLinkand descendant<Routes>(#9589, #9647) - Properly serialize/deserialize
ErrorResponseinstances when using built-in hydration (#9593) - Support
basenamein static data routers (#9591) - Updated dependencies:
@remix-run/router@1.0.4react-router@6.4.4
Full Changelog: v6.4.3...v6.4.4
- Generate correct
<a href>values when usingcreateHashRouter(#9409) - Better handle encoding/matching with special characters in URLs and route paths (#9477, #9496)
- Generate correct
formActionpathnames when anindexroute also has apath(#9486) - Respect
relative=pathprop onNavLink(#9453) - Fix
NavLinkbehavior for root urls (#9497) useRoutesshould be able to returnnullwhen passinglocationArg(#9485)- Fix
initialEntriestype increateMemoryRouter(#9498) - Support
basenameand relative routing inloader/actionredirects (#9447) - Ignore pathless layout routes when looking for proper submission
actionfunction (#9455) - Add UMD build for
@remix-run/router(#9446) - Fix
createURLin local file execution in Firefox (#9464)
Full Changelog: v6.4.2...v6.4.3
- Respect
basenameinuseFormAction(#9352) - Fix
IndexRouteObjectandNonIndexRouteObjecttypes to makehasErrorElementoptional (#9394) - Enhance console error messages for invalid usage of data router hooks (#9311)
- If an index route has children, it will result in a runtime error. We have strengthened our
RouteObject/RoutePropstypes to surface the error in TypeScript. (#9366)
Full Changelog: v6.4.1...v6.4.2
- Preserve state from
initialEntries(#9288) - Preserve
?indexfor fetcher get submissions to index routes (#9312)
Full Changelog: v6.4.0...v6.4.1
Whoa this is a big one! 6.4.0 brings all the data loading and mutation APIs over from Remix. Here's a quick high level overview, but it's recommended you go check out the docs, especially the feature overview and the tutorial.
New react-router APIs
- Create your router with
createMemoryRouter - Render your router with
<RouterProvider> - Load data with a Route
loaderand mutate with a Routeaction - Handle errors with Route
errorElement - Defer non-critical data with
deferandAwait
New react-router-dom APIs
- Create your router with
createBrowserRouter/createHashRouter - Submit data with the new
<Form>component - Perform in-page data loads and mutations with
useFetcher() - Defer non-critical data with
deferandAwait - Manage scroll position with
<ScrollRestoration> - Perform path-relative navigations with
<Link relative="path">(#9160)
- Path resolution is now trailing slash agnostic (#8861)
useLocationreturns the scoped location inside a<Routes location>component (#9094)- Respect the
<Link replace>prop if it is defined (#8779)
Full Changelog: v6.3.0...v6.4.0
- 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
- 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
- This release updates the internal
historydependency to5.2.0.
Full Changelog: v6.2.0...v6.2.1
- We now use statically analyzable CJS exports. This enables named imports in Node ESM scripts (See the commit).
- Fixed the
RoutePropselementtype, which should be aReactNode(#8473) - Fixed a bug with
useOutletfor top-level routes (#8483)
Full Changelog: v6.1.1...v6.2.0
- In v6.1.0 we inadvertently shipped a new, undocumented API that will likely introduce bugs (#7586). We have flagged
HistoryRouterasunstable_HistoryRouter, as this API will likely need to change before a new major release.
Full Changelog: v6.1.0...v6.1.1
<Outlet>can now receive acontextprop. This value is passed to child routes and is accessible via the newuseOutletContexthook. See the API docs for details. (#8461)<NavLink>can now receive a child function for access to its props. (#8164)- Improved TypeScript signature for
useMatchandmatchPath. For example, when you calluseMatch("foo/:bar/:baz"), the path is parsed and the return type will bePathMatch<"bar" | "baz">. (#8030)
- Fixed a bug that broke support for base64 encoded IDs on nested routes (#8291)
- A few error message improvements (#8202)
Full Changelog: v6.0.1...v6.1.0
- 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
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.