Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the current pathname correctly in React Router v6?

I am using a library called Material-UI, to build up my app layout. I am using its <ListItemButton> component inside the sidenav of my app. Now. this component <ListItemButton> is not a link, its a button. but it has some styles, you can see how it looks here, click me. This component accepts a component prop, what this component prop does is that it basically allows you to render something inside of it, for instance, I can give it <div/>, I can give it <a/> I can give it<b/> etc etc, but I can also give it a React component. and that's what I am doing here. I am giving it the React router <NavLink> component.

Now. this is so easy, just give it the <NavLink> component and its going to work. But, unfortunately, it doesn't allow you to write <NavLink> as <NavLink>, you must write it as NavLink without the brackets, therefore, I can not pass any arguments to it.

Luckily, the way this Material-UI works, is that when it sees a prop inside one of its components such as the <ListItemButton> component that it doesn't understand, it automatically passes that prop to the child component. so in our case, if we wrote a prop that is not inside the <ListItemButton> documentation, it's going to pass it to the NavLink component.

hence, we can pass the to prop. as follows:

<ListItemButton to="/users" component={NavLink}>

So far so good. The last thing we have to make is to make sure that the ListItemButton component is going to be highlighted whenever the link in the search bar matches the NavLink specified path.

The good news is that the <ListItemButton> has a predefined prop that makes it looks on the screen as selected, so it highlights it a bit darker. this prop is called the selected prop, it accepts a Boolean, (true or false). SO:

How can we do this? we can do it by comparing if the URL in the searchbar matches the path we specified in the to prop.

Now this was the introduction of why I want to find out the currently used path.

So here's the question:

as I know, the only way to do this is to compare if the currently visited link is the same path as the NavLink or Link component by doing something like so:

<ListItemButton  to="/users" component={NavLink} selected={pathname === "/users"}>

I know that this question was answered before, I saw the thread where people were mentioning that the way to do so is by using the useLocation() hook, they were saying you can do it like so: then do the comparison:

const { pathname } = useLocation()

However, this doesn't always work, for instance, if the user visited the following links, the ListItemButton component is Not going to be highlighted (selected):

  • http://example.com/users ✅ works
  • http://example.com/users/ ❌ doesn't work
  • http://example.com/users// ❌ doesn't work
  • http://example.com/users/// ❌ doesn't work

You might say, okay, simply compare using .includes() as follows:

<ListItemButton  to="/users" component={NavLink} selected={pathname.includes("/users")}>

But remember, this shouldn't be if you have other routes in the sidenav such as:

  • Manage Users (/users)
  • Create User (/users/create)

because if you did the comparison using the .includes() method, you'll end up highlighting (selecting) both NavLinks (ListItemButton).

Is there a built-in React Router Hook, or method that can get the route name correctly, the exact name that is defined by me in the <BrowserRouter> in my index.js file?

Full example in codesandbox

like image 285
Islam Y- Avatar asked Sep 17 '25 17:09

Islam Y-


1 Answers

I see, you are trying to style the button and not really the NavLink component if I'm understanding your post correctly. I think you may be looking for the useMatch hook.

Returns true if the URL for the given "to" value matches the current URL. This is useful for components that need to know "active" state, e.g. <NavLink>.

import { useMatch } from 'react-router-dom';

...

const isUserRoute = useMatch("/user");

...

<ListItemButton
  to="/users"
  component={NavLink}
  selected={isUserRoute}
>
  ...
</ListItemButton>

Update

To use the useMatch hook for each ListItemButton I suggest factoring ListItemButton out into its own component.

Example:

const LinkButton = ({ children, to }) => {
  const selected = useMatch(to);

  return (
    <ListItemButton
      component={NavLink}
      to={to}
      selected={selected}
    >
      {children}
    </ListItemButton>
  );
};

Use the LinkButton instead of ListItemButton in the UI.

<div>
  {listItems.map((item) => (
    <LinkButton key={item.label} to={item.path}>
      {item.label}
    </LinkButton>
  ))}
</div>

Edit how-to-get-the-current-pathname-correctly-in-react-router-v6

If you prefer keeping the path matching localized to the parent component, import and use the useLocation hook and matchPath utility from react-router-dom.

Example:

import { useLocation, matchPath } from 'react-router-dom';

...

const { pathname } = useLocation();

...

<div>
  {listItems.map((item) => (
    <ListItemButton
      component={NavLink}
      to={item.path}
      selected={matchPath(item.path, pathname)}
    >
      {item.label}
    </ListItemButton>
  ))}
</div>
like image 135
Drew Reese Avatar answered Sep 19 '25 07:09

Drew Reese