I have a router like this
import { createRoot } from 'react-dom/client';
// react-router v7
import { createHashRouter, Link, Outlet, RouterProvider } from 'react-router';
const root = document.createElement('div');
root.setAttribute('id', 'app');
document.body.appendChild(root);
const router = createHashRouter([
{
path: 'homepage',
element: (
<div>
<Link to="workspace/1">To workspace 1</Link>
<br />
<Link to="foo">To foo</Link>
<main>
<Outlet />
</main>
</div>
),
children: [
{
path: 'workspace/:spaceId/*',
// I want to lazy load this route, so I use <Routes> to define its sub routes
element: (
<div>
<Link to="setting">To space setting</Link>
<Routes>
<Route path="setting" />
</Routes>
</div>
),
},
{
path: 'foo',
element: <div>foo</div>,
},
],
},
]);
createRoot(root).render(
<RouterProvider router={router} />,
);
The <Link>
in homepage work fine. But when I click the 'To space setting' link multiple times, the path become to /homepage/workspace/1/setting/setting/setting...
.
I don't want to write absolute path in link eg. <Link to="/homepage/workspace/${spaceId}/setting" />
. Is there a better way to resolve this issue?
I have tried:
createHashRouter
to remove splat, the <Link>
works fine. But this is not what I want.<Link to={`${currentPath}/setting`} />
, but I didn't find an api for doing so in react-router
.It took a bit of digging through the React-Router v7 CHANGELOG and documentation, but the migration path from RR6 to RR7 relative paths (v7_relativeSplatPath
feature flag) is relatively straightforward.
https://reactrouter.com/upgrading/v6#v7_relativesplatpath
Split the
<Route>
into twoSplit any multi-segment splat
<Route>
into a parent route with the path and a child route with the splat
Old:
{
path: 'workspace/:spaceId/*',
element: (
<div>
<Link to="setting">To space setting</Link>
<Routes>
<Route path="setting" />
</Routes>
</div>
),
},
Current:
{
path: "workspace/:spaceId",
children: [
{
path: "*",
element: (
<div>
<Link to="setting">To space setting</Link>
<Routes>
<Route path="setting" element={<h1>Setting</h1>} />
</Routes>
</div>
),
},
],
},
Update relative links
Update any
<Link>
elements within that route tree to include the extra..
relative segment to continue linking to the same place
Old:
<Link to="setting">To space setting</Link>
Current:
<Link to="../setting">To space setting</Link>
The final key was getting the descendent route element to also render on exactly "/homepage/workspace/:spaceId"
. For this, make the "workspace/:spaceId"
route also an index route.
Final code:
const router = createHashRouter([
{
path: "homepage",
element: (
<div>
<Link to="workspace/1">To workspace 1</Link>
<br />
<Link to="foo">To foo</Link>
<main>
<Outlet />
</main>
</div>
),
children: [
{
path: "workspace/:spaceId",
children: [
{
index: true,
path: "*",
element: (
<div>
<Link to="../setting">To space setting</Link>
<Routes>
<Route path="setting" element={<h1>Setting</h1>} />
</Routes>
</div>
),
},
],
},
{
path: "foo",
element: <div>foo</div>,
},
],
},
]);