reactjsreact-router

How to use react router Link in a splat route?


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:


Solution

  • 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

    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>,
          },
        ],
      },
    ]);