@remix-run/router
1.23.2
Patch Changes
- Validate redirect locations (#14707)
1.23.1
Patch Changes
- Normalize double-slashes in
resolvePath(#14537)
1.23.0
Minor Changes
- Add
fetcherKeyas a parameter topatchRoutesOnNavigation(#13109)
Patch Changes
- Fix regression introduced in
6.29.0via #12169 that caused issues navigating to hash routes inside splat routes for applications using Lazy Route Discovery (patchRoutesOnNavigation) (#13108)
1.22.0
Minor Changes
- Provide the request
signalas a parameter topatchRoutesOnNavigation(#12900)
- This can be used to abort any manifest fetches if the in-flight navigation/fetcher is aborted
Patch Changes
- Do not log v7 deprecation warnings in production builds (#12794)
- Strip search parameters from
patchRoutesOnNavigationpathparam for fetcher calls (#12899) - Properly bubble headers when throwing a
data()result (#12845) - Optimize route matching by skipping redundant
matchRoutescalls when possible (#12169)
1.21.1
Patch Changes
- - Fix issue with fetcher data cleanup in the data layer on fetcher unmount (#12674)
- Fix behavior of manual fetcher keys when not opted into future.v7_fetcherPersist
1.21.0
Minor Changes
- - Log deprecation warnings for v7 flags (#11750)
- Add deprecation warnings to json/defer in favor of returning raw objects
- These methods will be removed in React Router v7
Patch Changes
- Update JSDoc URLs for new website structure (add /v6/ segment) (#12141)
1.20.0
Minor Changes
- Stabilize
unstable_patchRoutesOnNavigation(#11973)
- Add new PatchRoutesOnNavigationFunctionArgs type for convenience (#11967)
- Stabilize
unstable_dataStrategy(#11974) - Stabilize the
unstable_flushSyncoption for navigations and fetchers (#11989) - Stabilize the
unstable_viewTransitionoption for navigations and the correspondingunstable_useViewTransitionStatehook (#11989)
Patch Changes
- Fix bug when submitting to the current contextual route (parent route with an index child) when an
?indexparam already exists from a prior submission (#12003) - Fix
useFormActionbug - when removing?indexparam it would not keep other non-Remixindexparams (#12003) - Fix bug with fetchers not persisting
preventScrollResetthrough redirects during concurrent fetches (#11999) - Remove internal cache to fix issues with interrupted
patchRoutesOnNavigationcalls (#12055)
- We used to cache in-progress calls to patchRoutesOnNavigation internally so that multiple navigations with the same start/end would only execute the function once and use the same promise
- However, this approach was at odds with patch short circuiting if a navigation was interrupted (and the request.signal aborted) since the first invocation's patch would no-op
- This cache also made some assumptions as to what a valid cache key might be - and is oblivious to any other application-state changes that may have occurred
- So, the cache has been removed because in _most_ cases, repeated calls to something like import() for async routes will already be cached automatically - and if not it's easy enough for users to implement this cache in userland
- Avoid unnecessary
console.erroron fetcher abort due to back-to-back revalidation calls (#12050) - Expose errors thrown from
patchRoutesOnNavigationdirectly touseRouteErrorinstead of wrapping them in a 400ErrorResponseinstance (#12111) - Fix types for
RouteObjectwithinPatchRoutesOnNavigationFunction'spatchmethod so it doesn't expect agnostic route objects passed topatch(#11967) - Fix bugs with
partialHydrationwhen hydrating with errors (#12070) - Remove internal
discoveredRoutesFIFO queue fromunstable_patchRoutesOnNavigation(#11977)
1.19.2
Patch Changes
- Update the
unstable_dataStrategyAPI to allow for more advanced implementations (#11943)
- Rename unstable_HandlerResult to unstable_DataStrategyResult
- The return signature has changed from a parallel array of unstable_DataStrategyResult[] (parallel to matches) to a key/value object of routeId => unstable_DataStrategyResult
- This allows you to more easily decide to opt-into or out-of revalidating data that may not have been revalidated by default (via match.shouldLoad)
- ⚠️ This is a breaking change if you've currently adopted unstable_dataStrategy
- Added a new fetcherKey parameter to unstable_dataStrategy to allow differentiation from navigational and fetcher calls
- You should now return/throw a result from your handlerOverride instead of returning a DataStrategyResult
- If you are aggregating the results of match.resolve() into a final results object you should not need to think about the DataStrategyResult type
- If you are manually filling your results object from within your handlerOverride, then you will need to assign a DataStrategyResult as the value so React Router knows if it's a successful execution or an error.
- Preserve view transition through redirects (#11925)
- Fix blocker usage when
blocker.proceedis called quickly/synchronously (#11930) - Preserve pending view transitions through a router revalidation call (#11917)
1.19.1
Patch Changes
- Fog of War: Update
unstable_patchRoutesOnMisslogic so that we call the method when we match routes with dynamic param or splat segments in case there exists a higher-scoring static route that we've not yet discovered. (#11883)
- We also now leverage an internal FIFO queue of previous paths we've already called unstable_patchRouteOnMiss against so that we don't re-call on subsequent navigations to the same path
- Rename
unstable_patchRoutesOnMisstounstable_patchRoutesOnNavigationto match new behavior (#11888)
1.19.0
Minor Changes
- Add a new
replace(url, init?)alternative toredirect(url, init?)that performs ahistory.replaceStateinstead of ahistory.pushStateon client-side navigation redirects (#11811) - Add a new
unstable_data()API for usage with Remix Single Fetch (#11836)
- This API is not intended for direct usage in React Router SPA applications
- It is primarily intended for usage with createStaticHandler.query() to allow loaders/actions to return arbitrary data + status/headers without forcing the serialization of data into a Response instance
- This allows for more advanced serialization tactics via unstable_dataStrategy such as serializing via turbo-stream in Remix Single Fetch
- ⚠️ This removes the status field from HandlerResult
- If you need to return a specific status from unstable_dataStrategy you should instead do so via unstable_data()
Patch Changes
- Fix internal cleanup of interrupted fetchers to avoid invalid revalidations on navigations (#11839)
- When a fetcher.load is interrupted by an action submission, we track it internally and force revalidation once the action completes
- We previously only cleared out this internal tracking info on a successful _navigation_ submission
- Therefore, if the fetcher.load was interrupted by a fetcher.submit, then we wouldn't remove it from this internal tracking info on successful load (incorrectly)
- And then on the next navigation it's presence in the internal tracking would automatically trigger execution of the fetcher.load again, ignoring any shouldRevalidate logic
- This fix cleans up the internal tracking so it applies to both navigation submission and fetcher submissions
- Fix initial hydration behavior when using
future.v7_partialHydrationalong withunstable_patchRoutesOnMiss(#11838)
- During initial hydration, router.state.matches will now include any partial matches so that we can render ancestor HydrateFallback components
1.18.0
Minor Changes
- Stabilize
future.unstable_skipActionErrorRevalidationasfuture.v7_skipActionErrorRevalidation(#11769)
- When this flag is enabled, actions will not automatically trigger a revalidation if they return/throw a Response with a 4xx/5xx status code
- You may still opt-into revalidation via shouldRevalidate
- This also changes shouldRevalidate's unstable_actionStatus parameter to actionStatus
Patch Changes
- Fix bubbling of errors thrown from
unstable_patchRoutesOnMiss(#11786) - Fix hydration in SSR apps using
unstable_patchRoutesOnMissthat matched a splat route on the server (#11790)
1.17.1
Patch Changes
- Fog of War (unstable): Trigger a new
router.routesidentity/reflow during route patching (#11740) - Fog of War (unstable): Fix initial matching when a splat route matches (#11759)
1.17.0
Minor Changes
- Add support for Lazy Route Discovery (a.k.a. Fog of War) (#11626)
- RFC: <https://github.com/remix-run/react-router/discussions/11113>
- unstable_patchRoutesOnMiss docs: <https://reactrouter.com/v6/routers/create-browser-router>
1.16.1
Patch Changes
- Support
unstable_dataStrategyonstaticHandler.queryRoute(#11515)
1.16.0
Minor Changes
- Add a new
unstable_dataStrategyconfiguration option (#11098)
- This option allows Data Router applications to take control over the approach for executing route loaders and actions
- 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, middleware/context APIs, automatic loader caching, and more
- Move
unstable_dataStrategyfromcreateStaticHandlertostaticHandler.queryso it can be request-specific for use with theResponseStubapproach in Remix. It's not really applicable toqueryRoutefor now since that's a singular handler call anyway so any pre-processing/post/processing could be done there manually. (#11377) - Add a new
future.unstable_skipActionRevalidationfuture flag (#11098)
- Currently, active loaders revalidate after any action, regardless of the result
- With this flag enabled, actions that return/throw a 4xx/5xx response status will no longer automatically revalidate
- This should reduce load on your server since it's rare that a 4xx/5xx should actually mutate any data
- If you need to revalidate after a 4xx/5xx result with this flag enabled, you can still do that via returning true from shouldRevalidate
- shouldRevalidate 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
- Added a
skipLoaderErrorBubblingflag tostaticHandler.queryto disable error bubbling on loader executions for single-fetch scenarios where the client-side router will handle the bubbling (#11098)
1.15.3
Patch Changes
- Fix a
future.v7_partialHydrationbug that would re-run loaders below the boundary on hydration if SSR loader errors bubbled to a parent boundary (#11324) - Fix a
future.v7_partialHydrationbug that would consider the router uninitialized if a route did not have a loader (#11325)
1.15.2
Patch Changes
- Preserve hydrated errors during partial hydration runs (#11305)
1.15.1
Patch Changes
- Fix encoding/decoding issues with pre-encoded dynamic parameter values (#11199)
1.15.0
Minor Changes
- Add a
createStaticHandlerfuture.v7_throwAbortReasonflag to throwrequest.signal.reason(defaults to aDOMException) when a request is aborted instead of anErrorsuch asnew Error("query() call aborted: GET /path")(#11104)
- Please note that DOMException was added in Node v17 so you will not get a DOMException on Node 16 and below.
Patch Changes
- Respect the
ErrorResponsestatus code if passed togetStaticContextFormError(#11213)
1.14.2
Patch Changes
- Fix bug where dashes were not picked up in dynamic parameter names (#11160)
- Do not attempt to deserialize empty JSON responses (#11164)
1.14.1
Patch Changes
- Fix bug with
route.lazynot working correctly on initial SPA load whenv7_partialHydrationis specified (#11121) - Fix bug preventing revalidation from occurring for persisted fetchers unmounted during the
submittingphase (#11102) - De-dup relative path logic in
resolveTo(#11097)
1.14.0
Minor Changes
- Added a new
future.v7_partialHydrationfuture flag that enables partial hydration of a data router when Server-Side Rendering. This allows you to providehydrationData.loaderDatathat has values for _some_ initially matched route loaders, but not all. When this flag is enabled, the router will callloaderfunctions for routes that do not have hydration loader data duringrouter.initialize(), and it will render down to the deepest providedHydrateFallback(up to the first route without hydration data) while it executes the unhydrated routes. (#11033)
For example, the following router has a root and index route, but only provided hydrationData.loaderData for the root route. Because the index route has a loader, we need to run that during initialization. With future.v7_partialHydration specified, <RouterProvider> will render the RootComponent (because it has data) and then the IndexFallback (since it does not have data). Once indexLoader finishes, application will update and display IndexComponent.
```jsx
let router = createBrowserRouter(
[
{
id: "root",
path: "/",
loader: rootLoader,
Component: RootComponent,
Fallback: RootFallback,
children: [
{
id: "index",
index: true,
loader: indexLoader,
Component: IndexComponent,
HydrateFallback: IndexFallback,
},
],
},
],
{
future: {
v7_partialHydration: true,
},
hydrationData: {
loaderData: {
root: { message: "Hydrated from Root!" },
},
},
}
);
```
If the above example did not have an IndexFallback, then RouterProvider would instead render the RootFallback while it executed the indexLoader.
Note: When future.v7_partialHydration is provided, the <RouterProvider fallbackElement> prop is ignored since you can move it to a Fallback on your top-most route. The fallbackElement prop will be removed in React Router v7 when v7_partialHydration behavior becomes the standard behavior.
- Add a new
future.v7_relativeSplatPathflag to implement a breaking bug fix to relative routing when inside a splat route. (#11087)
This fix was originally added in #10983 and was later reverted in #11078 because it was determined that a large number of existing applications were relying on the buggy behavior (see #11052)
The Bug
The buggy behavior is that without this flag, the default behavior when resolving relative paths is to _ignore_ any splat (*) portion of the current route path.
The Background
This decision was originally made thinking that it would make the concept of nested different sections of your apps in <Routes> easier if relative routing would _replace_ the current splat:
```jsx
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard/*" element={<Dashboard />} />
</Routes>
</BrowserRouter>
```
Any paths like /dashboard, /dashboard/team, /dashboard/projects will match the Dashboard route. The dashboard component itself can then render nested <Routes>:
```jsx
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
<nav>
<Link to="/">Dashboard Home</Link>
<Link to="team">Team</Link>
<Link to="projects">Projects</Link>
</nav>
<Routes>
<Route path="/" element={<DashboardHome />} />
<Route path="team" element={<DashboardTeam />} />
<Route path="projects" element={<DashboardProjects />} />
</Routes>
</div>
);
}
```
Now, all links and route paths are relative to the router above them. This makes code splitting and compartmentalizing your app really easy. You could render the Dashboard as its own independent app, or embed it into your large app without making any changes to it.
The Problem
The problem is that this concept of ignoring part of a path breaks a lot of other assumptions in React Router - namely that "." always means the current location pathname for that route. When we ignore the splat portion, we start getting invalid paths when using ".":
```jsx
// If we are on URL /dashboard/team, and we want to link to /dashboard/team:
function DashboardTeam() {
// ❌ This is broken and results in <a href="/dashboard">
return <Link to=".">A broken link to the Current URL</Link>;
// ✅ This is fixed but super unintuitive since we're already at /dashboard/team!
return <Link to="./team">A broken link to the Current URL</Link>;
}
```
We've also introduced an issue that we can no longer move our DashboardTeam component around our route hierarchy easily - since it behaves differently if we're underneath a non-splat route, such as /dashboard/:widget. Now, our "." links will, properly point to ourself _inclusive of the dynamic param value_ so behavior will break from it's corresponding usage in a /dashboard/* route.
Even worse, consider a nested splat route configuration:
```jsx
<BrowserRouter>
<Routes>
<Route path="dashboard">
<Route path="*" element={<Dashboard />} />
</Route>
</Routes>
</BrowserRouter>
```
Now, a <Link to="."> and a <Link to=".."> inside the Dashboard component go to the same place! That is definitely not correct!
Another common issue arose in Data Routers (and Remix) where any <Form> should post to it's own route action if you the user doesn't specify a form action:
```jsx
let router = createBrowserRouter({
path: "/dashboard",
children: [
{
path: "*",
action: dashboardAction,
Component() {
// ❌ This form is broken! It throws a 405 error when it submits because
// it tries to submit to /dashboard (without the splat value) and the parent
// /dashboard route doesn't have an action
return <Form method="post">...</Form>;
},
},
],
});
```
This is just a compounded issue from the above because the default location for a Form to submit to is itself (".") - and if we ignore the splat portion, that now resolves to the parent route.
The Solution
If you are leveraging this behavior, it's recommended to enable the future flag, move your splat to it's own route, and leverage ../ for any links to "sibling" pages:
```jsx
<BrowserRouter>
<Routes>
<Route path="dashboard">
<Route index path="*" element={<Dashboard />} />
</Route>
</Routes>
</BrowserRouter>
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
<nav>
<Link to="..">Dashboard Home</Link>
<Link to="../team">Team</Link>
<Link to="../projects">Projects</Link>
</nav>
<Routes>
<Route path="/" element={<DashboardHome />} />
<Route path="team" element={<DashboardTeam />} />
<Route path="projects" element={<DashboardProjects />} />
</Router>
</div>
);
}
```
This way, . means "the full current pathname for my route" in all cases (including static, dynamic, and splat routes) and .. always means "my parents pathname".
Patch Changes
- 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)
1.13.1
Patch Changes
- Revert the
useResolvedPathfix for splat routes due to a large number of applications that were relying on the buggy behavior (see <https://github.com/remix-run/react-router/issues/11052#issuecomment-1836589329>). We plan to re-introduce this fix behind a future flag in the next minor version. (#11078)
1.13.0
Minor Changes
- Export the
PathParamtype from the public API (#10719)
Patch Changes
- Fix bug with
resolveToin splat routes (#11045)
- This is a follow up to #10983 to handle the few other code paths using getPathContributingMatches
- This removes the UNSAFE_getPathContributingMatches export from @remix-run/router since we no longer need this in the react-router/react-router-dom layers
- Do not revalidate unmounted fetchers when
v7_fetcherPersistis enabled (#11044)
1.12.0
Minor Changes
- Add
unstable_flushSyncoption torouter.navigateandrouter.fetchto tell the React Router layer to opt-out ofReact.startTransitionand intoReactDOM.flushSyncfor state updates (#11005)
Patch Changes
- Fix
relative="path"bug where relative path calculations started from the full location pathname, instead of from the current contextual route pathname. (#11006)
```jsx
<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 />
</>
);
}
```
1.11.0
Minor Changes
- Add a new
future.v7_fetcherPersistflag to the@remix-run/routerto change the persistence behavior of fetchers whenrouter.deleteFetcheris called. Instead of being immediately cleaned up, fetchers will persist until they return to anidlestate (RFC) (#10962)
- 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 an idle state
- Keep an eye out for the following specific behavioral changes when opting into this flag and check your app for compatibility:
- Fetchers that complete _while still mounted_ will no longer appear in useFetchers(). They served effectively no purpose in there since you can access the data via useFetcher().data).
- Fetchers that previously unmounted _while in-flight_ will not be immediately aborted and will instead be cleaned up once they return to an idle state. They will remain exposed via useFetchers while in-flight so you can still access pending/optimistic data after unmount.
- When
v7_fetcherPersistis enabled, the router now performs ref-counting on fetcher keys viagetFetcher/deleteFetcherso it knows when a given fetcher is totally unmounted from the UI (#10977)
- Once a fetcher has been totally unmounted, we can ignore post-processing of a persisted fetcher result such as a redirect or an error
- The router will also pass a new deletedFetchers array to the subscriber callbacks so that the UI layer can remove associated fetcher data
- Add support for optional path segments in
matchPath(#10768)
Patch Changes
- Fix
router.getFetcher/router.deleteFetchertype definitions which incorrectly specifiedkeyas an optional parameter (#10960)
1.10.0
Minor Changes
- Add experimental support for the View Transitions API by allowing users to opt-into view transitions on navigations via the new
unstable_viewTransitionoption torouter.navigate(#10916)
Patch Changes
- Allow 404 detection to leverage root route error boundary if path contains a URL segment (#10852)
- Fix
ErrorResponsetype to avoid leaking internal field (#10876)
1.9.0
Minor Changes
- In order to move towards stricter TypeScript support in the future, we're aiming to replace current usages of
anywithunknownon exposed typings for user-provided data. To do this in Remix v2 without introducing breaking changes in React Router v6, we have added generics to a number of shared types. These continue to default toanyin React Router and are overridden withunknownin Remix. In React Router v7 we plan to move these tounknownas a breaking change. (#10843)
- Location now accepts a generic for the location.state value
- ActionFunctionArgs/ActionFunction/LoaderFunctionArgs/LoaderFunction now accept a generic for the context parameter (only used in SSR usages via createStaticHandler)
- The return type of useMatches (now exported as UIMatch) accepts generics for match.data and match.handle - both of which were already set to unknown
- Move the
@privateclass exportErrorResponseto anUNSAFE_ErrorResponseImplexport since it is an implementation detail and there should be no construction ofErrorResponseinstances in userland. This frees us up to export atype ErrorResponsewhich correlates to an instance of the class viaInstanceType. Userland code should only ever be usingErrorResponseas a type and should be type-narrowing viaisRouteErrorResponse. (#10811) - Export
ShouldRevalidateFunctionArgsinterface (#10797) - Removed private/internal APIs only required for the Remix v1 backwards compatibility layer and no longer needed in Remix v2 (
_isFetchActionRedirect,_hasFetcherDoneAnything) (#10715)
Patch Changes
- 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)
1.8.0
Minor Changes
- Add's a new
redirectDocument()function which allows users to specify that a redirect from aloader/actionshould trigger a document reload (viawindow.location) instead of attempting to navigate to the redirected location via React Router (#10705)
Patch Changes
- Fix an issue in
queryRoutethat was not always identifying thrownResponseinstances (#10717) - Ensure hash history always includes a leading slash on hash pathnames (#10753)
1.7.2
Patch Changes
- 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, #10709)
- Initial-load fetchers should not automatically revalidate on GET navigations (#10688)
- Enhance the return type of
Route.lazyto prohibit returning an empty object (#10634)
1.7.1
Patch Changes
- Fix issues with reused blockers on subsequent navigations (#10656)
1.7.0
Minor Changes
- Add support for
application/jsonandtext/plainencodings forrouter.navigate/router.fetchsubmissions. To leverage these encodings, pass your data in abodyparameter and specify the desiredformEncType: (#10413)
```js
// By default, the encoding is "application/x-www-form-urlencoded"
router.navigate("/", {
formMethod: "post",
body: { key: "value" },
});
async function action({ request }) {
// await request.formData() => FormData instance with entry [key=value]
}
```
```js
// Pass formEncType to opt-into a different encoding (json)
router.navigate("/", {
formMethod: "post",
formEncType: "application/json",
body: { key: "value" },
});
async function action({ request }) {
// await request.json() => { key: "value" }
}
```
```js
// Pass formEncType to opt-into a different encoding (text)
router.navigate("/", {
formMethod: "post",
formEncType: "text/plain",
body: "Text submission",
});
async function action({ request }) {
// await request.text() => "Text submission"
}
```
Patch Changes
- Call
window.history.pushState/replaceStatebefore updating React Router state (instead of after) so thatwindow.locationmatchesuseLocationduring synchronous React 17 rendering (#10448)
- ⚠️ However, generally apps should not be relying on 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.)
- Strip
basenamefrom thelocationprovided to<ScrollRestoration getKey>to match theuseLocationbehavior (#10550) - Avoid calling
shouldRevalidatefor fetchers that have not yet completed a data load (#10623) - Fix
unstable_useBlockerkey issues inStrictMode(#10573) - Upgrade
typescriptto 5.1 (#10581)
1.6.3
Patch Changes
- Allow fetcher revalidations to complete if submitting fetcher is deleted (#10535)
- Re-throw
DOMException(DataCloneError) when attempting to perform aPUSHnavigation with non-serializable state. (#10427) - Ensure revalidations happen when hash is present (#10516)
- upgrade jest and jsdom (#10453)
1.6.2
Patch Changes
- Fix HMR-driven error boundaries by properly reconstructing new routes and
manifestin\_internalSetRoutes(#10437) - Fix bug where initial data load would not kick off when hash is present (#10493)
1.6.1
Patch Changes
- 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)
1.6.0
Minor Changes
- Enable relative routing in the
@remix-run/routerwhen providing a source route ID from which the path is relative to: (#10336)
- Example: router.navigate("../path", { fromRouteId: "some-route" }).
- This also applies to router.fetch which already receives a source route ID
- Introduce a new
@remix-run/routerfuture.v7_prependBasenameflag to enablebasenameprefixing to all paths coming intorouter.navigateandrouter.fetch.
- Previously the basename was prepended in the React Router layer, but now that relative routing is being handled by the router we need prepend the basename _after_ resolving any relative paths
- This also enables basename support in useFetcher as well
Patch Changes
- 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) - Deprecate the
createRouterdetectErrorBoundaryoption in favor of the newmapRoutePropertiesoption for converting a framework-agnostic route to a framework-aware route. This allows us to set more than just thehasErrorBoundaryproperty during route pre-processing, and is now used for mappingComponent -> elementandErrorBoundary -> errorElementinreact-router. (#10287) - Fixed a bug where fetchers were incorrectly attempting to revalidate on search params changes or routing to the same URL (using the same logic for route
loaderrevalidations). However, since fetchers have a static href, they should only revalidate onactionsubmissions orrouter.revalidatecalls. (#10344) - 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)
1.5.0
Minor Changes
- Added support for Future Flags in React Router. The first flag being introduced is
future.v7_normalizeFormMethodwhich will normalize the exposeduseNavigation()/useFetcher()formMethodfields as uppercase HTTP methods to align with thefetch()behavior. (#10207)
- When future.v7_normalizeFormMethod === false (default v6 behavior),
- useNavigation().formMethod is lowercase
- useFetcher().formMethod is lowercase
- When future.v7_normalizeFormMethod === true:
- useNavigation().formMethod is uppercase
- useFetcher().formMethod is uppercase
Patch Changes
- 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)
1.4.0
Minor Changes
- 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.
```jsx
// 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:
```jsx
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.
🙌 Huge thanks to @rossipedia for the Initial Proposal and POC Implementation.
Patch Changes
- Fix
generatePathincorrectly applying parameters in some cases (#10078)
1.3.3
Patch Changes
- Correctly perform a hard redirect for same-origin absolute URLs outside of the router
basename(#10076) - 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) - Add internal API for custom HMR implementations (#9996)
1.3.2
Patch Changes
- Remove inaccurate console warning for POP navigations and update active blocker logic (#10030)
- Only check for differing origin on absolute URL redirects (#10033)
1.3.1
Patch Changes
- Fixes 2 separate issues for revalidating fetcher
shouldRevalidatecalls (#9948)
- The 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.
- The parameters being passed were incorrect and inconsistent with one another since the 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.
- Respect
preventScrollReseton<fetcher.Form>(#9963) - Do not short circuit on hash change only mutation submissions (#9944)
- Remove
instanceofcheck fromisRouteErrorResponseto avoid bundling issues on the server (#9930) - Fix navigation for hash routers on manual URL changes (#9980)
- Detect when a
defercall only contains critical data and remove theAbortController(#9965) - Send the name as the value when url-encoding
FileFormDataentries (#9867)
1.3.0
Minor Changes
- Added support for navigation blocking APIs (#9709)
- Expose deferred information from
createStaticHandler(#9760)
Patch Changes
- Improved absolute redirect url detection in actions/loaders (#9829)
- Fix URL creation with memory histories (#9814)
- Fix
generatePathwhen optional params are present (#9764) - Fix scroll reset if a submission redirects (#9886)
- Fix 404 bug with same-origin absolute redirects (#9913)
- Support
OPTIONSrequests instaticHandler.queryRoute(#9914)
1.2.1
Patch Changes
- Include submission info in
shouldRevalidateon action redirects (#9777, #9782) - Reset
actionDataon action redirect to current location (#9772)
1.2.0
Minor Changes
- Remove
unstable_prefix fromcreateStaticHandler/createStaticRouter/StaticRouterProvider(#9738)
Patch Changes
- Fix explicit
replaceon submissions andPUSHon submission to new paths (#9734) - Fix a few bugs where loader/action data wasn't properly cleared on errors (#9735)
- Prevent
useLoaderDatausage inerrorElement(#9735) - Skip initial scroll restoration for SSR apps with
hydrationData(#9664)
1.1.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
- Path
lang?/aboutwill match:
- /:lang/about
- /about
- Path
/multistep/:widget1?/widget2?/widget3?will match:
- /multistep
- /multistep/:widget1
- /multistep/:widget1/:widget2
- /multistep/:widget1/:widget2/:widget3
Optional Static Segment Example
- Path
/home?will match:
- /
- /home
- Path
/fr?/aboutwill match:
- /about
- /fr/about
Minor Changes
- Allows optional routes and optional static segments (#9650)
Patch Changes
- Stop incorrectly matching on partial named parameters, i.e.
<Route path="prefix-:param">, to align with how splat parameters work. If you were previously relying on this behavior then it's recommended to extract the static portion of the path at theuseParamscall site: (#9506)
```jsx
// 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)
1.0.5
Patch Changes
- Fix requests sent to revalidating loaders so they reflect a
GETrequest (#9680) - Remove
instanceof Responsechecks in favor ofisResponse(#9690) - Fix
URLcreation in Cloudflare Pages or other non-browser-environments (#9682, #9689) - Add
requestContextsupport to static handlerquery/queryRoute(#9696)
- Note that the unstable API of queryRoute(path, routeId) has been changed to queryRoute(path, { routeId, requestContext })
1.0.4
Patch Changes
- Throw an error if an
action/loaderfunction returnsundefinedas revalidations need to know whether the loader has previously been executed.undefinedalso causes issues during SSR stringification for hydration. You should always ensure youloader/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
1.0.3
Patch Changes
- Fix hrefs generated when using
createHashRouter(#9409) - fix encoding/matching issues with special chars (#9477, #9496)
- Support
basenameand relative routing inloader/actionredirects (#9447) - Ignore pathless layout routes when looking for proper submission
actionfunction (#9455) - properly support
indexroutes with apathinuseResolvedPath(#9486) - Add UMD build for
@remix-run/router(#9446) - fix
createURLin local file execution in Firefox (#9464) - Updates to
unstable_createStaticHandlerfor incorporating into Remix (#9482, #9465)
1.0.2
Patch Changes
- Reset
actionDataafter a successful action redirect (#9334) - Update
matchPathto avoid false positives on dash-separated segments (#9300) - 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)
1.0.1
Patch Changes
- Preserve state from
initialEntries(#9288) - Preserve
?indexfor fetcher get submissions to index routes (#9312)
1.0.0
This is the first stable release of @remix-run/router, which provides all the underlying routing and data loading/mutation logic for react-router. You should _not_ be using this package directly unless you are authoring a routing library similar to react-router.
For an overview of the features provided by react-router, we recommend you go check out the docs, especially the feature overview and the tutorial.
For an overview of the features provided by @remix-run/router, please check out the README.