Search code examples
javascriptreactjsreact-routerreact-router-dombreadcrumbs

React-router v6 breadcrumbs and partially matching routes


I am working with react-router-dom v6.8.1 (newest version as of now), and previously had a working breadcrumb setup using this third-party lib called use-react-router-breadcrumbs, but according to it's doc, they are now instead recommending doing it the "built-in react-router way", that is documented here. It's based on attaching a crumb to the handle object of each route, and retrieve it using the useMatches hook.

So I rewrote the code, but it has a quite major flaw that I cannot get around. Say that I have 3 routes, where 2 and 3 is nested below 1:

{
    path: '/',
    element: <Layout />,
    handle: {
      crumb: () => 'Home',
    },
    children: [
      {
        path: '/users',
        element: <UserList />,
        handle: {
          crumb: () => 'Users',
        },
      },
      {
        path: '/users/:id',
        element: <UserDetails />,
        handle: {
          crumb: () => <DynamicUserNameCrumb />,
        },
      },
   ]
}

With the custom lib you can go to /users/:id and get a breadcrumb for each one of these routes, making the entire breadcrumbs look like:

"Home -> Users -> John Doe"

However, when using the new built-in way with the useMatches() hook, I only get a match on route 1 and 3. Route 2 (/users) is not considered a match, and I cannot access the crumb for that route. Result is this, which is not what I want:

"Home -> John Doe"

So my question is: How are you supposed to handle this kind of situation? Nesting route 3 under 2 was my first idea, and this made the crumbs correct, but then it actually renders the component defined for route 2 (User list), and I only want it to render route 1 (layout) and 3 (User details page).

I was hoping that maybe useMatches() would be able to accept configuration for also returning partial matches, but it seems that this hook does not accept any input.

I am close to reverting and going back to the third party lib, but wanted to ask here before I do so, since they explicitly recommended using the native solution based on useMatches and a handle object. I figured there must be a solution for this if this is the officially recommended way to handle breadcrumbs in react-router


Solution

  • From what I can tell it is because "/users" and "/users/:id" are sibling routes. Refactor the routes config such that "/users/:id" is a child route of "/users" so there's a "logical path" and individual segments from "/" to "users" to ":id".

    Example:

    const router = createBrowserRouter([
      {
        path: "/",
        element: <Layout />,
        handle: {
          crumb: () => "Home"
        },
        children: [
          {
            path: "/users",
            handle: {
              crumb: () => "Users"
            },
            children: [
              {
                index: true,
                element: <UserList />
              },
              {
                path: "/users/:id",
                element: <UserDetails />,
                handle: {
                  crumb: () => <DynamicUserNameCrumb />
                }
              }
            ]
          }
        ]
      }
    ]);
    

    Edit react-router-v6-breadcrumbs-and-partially-matching-routes

    enter image description here