3

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

Knut Marius
  • 1,588
  • 18
  • 40

1 Answers1

3

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

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Yes! It seems I was not aware of the pattern where you define the parent without an element, and then use the "index:true" on the child to make the rendering correct. This works, so I'm going with this solution! Thanks a alot! – Knut Marius Feb 17 '23 at 07:17
  • @KnutMarius Yes, well, that was done so that the `UserList` could still be a "sibling" route and not need to render an `Outlet` for a nested route rendering `UserDetails`. Routes without an `element` prop implicitly render an `Outlet` component. Glad this works for you. Cheers and good luck! – Drew Reese Feb 17 '23 at 07:25
  • In these situations it's not an outlet/layout kind of situation between user list and user details. I always just want to render _one_ of them at the time. I was not sure if this was a kind of scenario where it was intended to use nested routes. I am quite sure I've seen examples from the past where this situation is treated with a flat route structure, and only the specificity of the paths decide which route to render. Do you know if the nested routes is considered best practice way currently? – Knut Marius Feb 17 '23 at 07:50
  • @KnutMarius Route nesting certainly makes *some* things easier, but I wouldn't go as far as to say it's a "best practice". Flat lists of all routes are still valid. The nested routes might just be a path towards making the code a little more DRY. – Drew Reese Feb 17 '23 at 07:53
  • Not that it's directly related, but I'm also working on a way to authorize different routes, and doing the nested way could perhaps also help. I could then create a `` layout which takes the required permissions and put that on the parent route (in this case /users). Then it would automatically protect all the child routes as well... – Knut Marius Feb 17 '23 at 07:56
  • 1
    @KnutMarius That's absolutely correct. I have an answer with route protection examples: https://stackoverflow.com/a/66289280/8690857. – Drew Reese Feb 17 '23 at 07:57