Search code examples
javascriptreactjstypescriptreact-routerreact-router-dom

How can I create a subrouter with React Router v6?


Here is my current React Router implementation:

const router = createBrowserRouter([
    {
      path: "/",
      element: (
        <Page activeNav="home" >
          <Home />
        </Page>
      )
    },
    {
      path: "/about",
      element: (
        <Page activeNav="about" >
          <About />
        </Page>
      )
    },
    {
      path: "/blog",
      element: (
        <Page activeNav="blog">
          <Blog />
        </Page>
      )
    },
    {
      path: "/blog/:postName",
      element: (
        <Page activeNav="blog" >
          <Post />
        </Page>
      ),
      loader: ({ params }) => params.postName
    },
    {
      path: "/chess",
      element: <ChessRouter />
    }
  ])

The last route, /chess is of importance. I am looking to define routes such as /chess/play, /chess/login, /chess/register, etc. My initial idea was to just put another Router as the element for the /chess path and then all those paths would be routed from there. However, that throws an error saying:

You cannot render a <Router> inside another <Router>. You should never have more than one in your app.

I also tried using the children property on the /chess route but this does not render anything when I go to /chess/play etc.

What is the correct way of implementing subpaths (not sure of the correct word for it)?


Solution

  • It is correct that you cannot render a router component within another router component as this is an invariant violation. You need only one router and routing context per React app.

    To render sub-routes on "/chess" then there are two options:

    1. Render nested routes in the routes configuration declaration. This requires the ChessRouter component to render an Outlet component for nested routes to render their element content into. Nested routes will be able to use the new RRDv6.4 Data APIs.

      const router = createBrowserRouter([
        ...,
        {
          path: "/chess",
          element: <ChessRouter />,
          children: [
            ...,
            {
              path: "play",
              element: <ChessPlay />
            },
            ... other chess sub-routes
          ],
        }
      ]);
      
      const ChessRouter = () => {
        ...
      
        return (
          ...
          <Outlet />
          ...
        );
      };
      
    2. Render a root route with trailing wildcard ("*") character that allows descendent routes to also be matched. This allows the ChessRouter component to render descendent routes, i.e. a Routes component with a set of Route components. Descendent routes will not be able to use the RRDv6.4 Data APIs.

      const router = createBrowserRouter([
        ...,
        {
          path: "/chess/*",
          element: <ChessRouter />,
        }
      ]);
      
      const ChessRouter = () => {
        ...
      
        return (
          ...
          <Routes>
            ...
            <Route path="/play" element={<ChessPlay />} />
            ... other chess sub-routes
          </Routes>
          ...
        );
      };