The following future flags are stable and ready to adopt. To read more about future flags see Development Strategy
First update to the latest minor version of v6.x to have the latest future flags.
👉 Update to latest v6
npm install react-router-dom@6
Background
Changes the relative path matching and linking for multi-segment splats paths like dashboard/*
(vs. just *
). View the CHANGELOG for more information.
👉 Enable the flag
Enabling the flag depends on the type of router:
<BrowserRouter
future={{
v7_relativeSplatPath: true,
}}
/>
createBrowserRouter(routes, {
future: {
v7_relativeSplatPath: true,
},
});
Update your Code
If you have any routes with a path + a splat like <Route path="dashboard/*">
and has relative links like <Link to="relative">
or <Link to="../relative">
beneath it, you will need to update your code.
👉 Split the <Route>
into two
Split any multi-segment splat <Route>
into a parent route with the path and a child route with the splat:
<Routes>
<Route path="/" element={<Home />} />
- <Route path="dashboard/*" element={<Dashboard />} />
+ <Route path="dashboard">
+ <Route index element={<Dashboard />} />
+ <Route path="*" element={<Dashboard />} />
+ </Route>
</Routes>
// or
createBrowserRouter([
{ path: "/", element: <Home /> },
{
- path: "dashboard/*",
- element: <Dashboard />,
+ path: "dashboard",
+ children: [{ path: "*", element: <Dashboard /> }],
},
]);
👉 Update relative links
Update any <Link>
elements within that route tree to include the extra ..
relative segment to continue linking to the same place:
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
<nav>
- <Link to="/">Dashboard Home</Link>
- <Link to="team">Team</Link>
- <Link to="projects">Projects</Link>
+ <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>
);
}
Background
This uses React.useTransition
instead of React.useState
for Router state updates. View the CHANGELOG for more information.
👉 Enable the flag
<BrowserRouter
future={{
v7_startTransition: true,
}}
/>
// or
<RouterProvider
future={{
v7_startTransition: true,
}}
/>
👉 Update your Code
You don't need to update anything unless you are using React.lazy
inside of a component.
Using React.lazy
inside of a component is incompatible with React.useTransition
(or other code that makes promises inside of components). Move React.lazy
to the module scope and stop making promises inside of components. This is not a limitation of React Router but rather incorrect usage of React.
createBrowserRouter
you can skip this
Background
The fetcher lifecycle is now based on when it returns to an idle state rather than when its owner component unmounts: View the CHANGELOG for more information.
Enable the Flag
createBrowserRouter(routes, {
future: {
v7_fetcherPersist: true,
},
});
Update your Code
It's unlikely to affect your app. You may want to check any usage of useFetchers
as they may persist longer than they did before. Depending on what you're doing, you may render something longer than before.
createBrowserRouter
you can skip this
This normalizes formMethod
fields as uppercase HTTP methods to align with the fetch()
behavior. View the CHANGELOG for more information.
👉 Enable the Flag
createBrowserRouter(routes, {
future: {
v7_normalizeFormMethod: true,
},
});
Update your Code
If any of your code is checking for lowercase HTTP methods, you will need to update it to check for uppercase HTTP methods (or call toLowerCase()
on it).
👉 Compare formMethod
to UPPERCASE
-useNavigation().formMethod === "post"
-useFetcher().formMethod === "get";
+useNavigation().formMethod === "POST"
+useFetcher().formMethod === "GET";
createBrowserRouter
you can skip this
This allows SSR frameworks to provide only partial hydration data. It's unlikely you need to worry about this, just turn the flag on. View the CHANGELOG for more information.
👉 Enable the Flag
createBrowserRouter(routes, {
future: {
v7_partialHydration: true,
},
});
Update your Code
With partial hydration, you need to provide a HydrateFallback
component to render during initial hydration. Additionally, if you were using fallbackElement
before, you need to remove it as it is now deprecated. In most cases, you will want to reuse the fallbackElement
as the HydrateFallback
.
👉 Replace fallbackElement
with HydrateFallback
const router = createBrowserRouter(
[
{
path: "/",
Component: Layout,
+ HydrateFallback: Fallback,
// or
+ hydrateFallbackElement: <Fallback />,
children: [],
},
],
);
<RouterProvider
router={router}
- fallbackElement={<Fallback />}
/>
createBrowserRouter
you can skip this
When this flag is enabled, loaders will no longer revalidate by default after an action throws/returns a Response
with a 4xx
/5xx
status code. You may opt-into revalidation in these scenarios via shouldRevalidate
and the actionStatus
parameter.
👉 Enable the Flag
createBrowserRouter(routes, {
future: {
v7_skipActionErrorRevalidation: true,
},
});
Update your Code
In most cases, you probably won't have to make changes to your app code. Usually, if an action errors, it's unlikely data was mutated and needs revalidation. If any of your code does mutate data in action error scenarios you have 2 options:
👉 Option 1: Change the action
to avoid mutations in error scenarios
// Before
async function action() {
await mutateSomeData();
if (detectError()) {
throw new Response(error, { status: 400 });
}
await mutateOtherData();
// ...
}
// After
async function action() {
if (detectError()) {
throw new Response(error, { status: 400 });
}
// All data is now mutated after validations
await mutateSomeData();
await mutateOtherData();
// ...
}
👉 Option 2: Opt-into revalidation via shouldRevalidate
and actionStatus
async function action() {
await mutateSomeData();
if (detectError()) {
throw new Response(error, { status: 400 });
}
await mutateOtherData();
}
async function loader() { ... }
function shouldRevalidate({ actionStatus, defaultShouldRevalidate }) {
if (actionStatus != null && actionStatus >= 400) {
// Revalidate this loader when actions return a 4xx/5xx status
return true;
}
return defaultShouldRevalidate;
}