Show parent NavLink as active in React Router V6

Viewed 25

I have a multi-level navigation, where the first child is the index route (e.g. the Company Details item is the index route for /companies):

Home     Companies     

Company Details     Users

This is my routes setup:

<Routes>
  <Route index element={<Home />} />
  <Route path="companies" element={<Companies />}>
    <Route index element={<CompaniesList />} />
    <Route path=":companyId" element={<Company />}>
      <Route index element={<CompanyDetail />} />
      <Route path="users" element={<Users />}>
        <Route index element={<UserList />} />
        <Route path=":userId" element={<UserDetail />} />
      </Route>
    </Route>
  </Route>
</Routes>

This essentially creates this route structure (note that Details and Users are siblings):

Companies:            /companies
  - Details:          /companies/:companyId
  - Users:            /companies/:companyId/users
      - User Details: /companies/:companyId/users/:userId

I'm using <NavLink> to set the active class for the current page in the navigation component:

<NavLink to="..." className={({ isActive }) => (isActive ? 'is-active' : '')} />

If I navigate to the Details page, everything works as expected. That nav item and the parent Companies nav item are both active.

If I navigate to the Users page (/companies/:companyId/users), all three items (Companies, Details, and Users) are active. I don't want the Details link to be active.

If I add the end prop to the <NavLink /> component (<NavLink ... end />), that fixes the Details link, but the Companies link is now not active. If I navigate to the User Details page, then nothing is active.

I've also tried using useLocation() to match the pathname, but this still matches on Details since /companies/:companyId/users includes /companies/:companyId:

const location = useLocation();

const isParent = location.pathname.includes(pathname);

Is there a way to determine if a route is a true parent of the active route? In my case, Details should not be considered a parent of Users, but Companies should.

1 Answers

Only add the end prop to the NavLink components you want to match when the path ends exactly with that target path.

Example:

<NavLink
  to="/companies" // <-- any "/companies/*
  className={({ isActive }) => (isActive ? "is-active" : "")}
>
  Companies List
</NavLink>
<NavLink
  end // <-- only when "/companies/:companyId"
  to="/companies/...."
  className={({ isActive }) => (isActive ? "is-active" : "")}
>
  ....
</NavLink>
<NavLink
  end // <-- only when "/companies/:companyId/users"
  to="/companies/..../users"
  className={({ isActive }) => (isActive ? "is-active" : "")}
>
  ....
</NavLink>
<NavLink
  end // <-- only when "/companies/:companyId/users/:userId"
  to="/companies/..../users/...."
  className={({ isActive }) => (isActive ? "is-active" : "")}
>
  ....
</NavLink>

Edit show-parent-navlink-as-active-in-react-router-v6

enter image description here enter image description here enter image description here enter image description here enter image description here

Related