react-router
react-router copied to clipboard
[v6] [Feature]: bring back isActive prop in NavLink component
What is the new or updated feature that you are suggesting?
NavLink's isActive prop has been removed in v6 either intentionally or accidentally. I suggest to bring it back.
Why should this feature be included?
isActive prop helps with use cases like when you need to highlight a link or a tab depending on url's query parameters
Thanks for bringing this to our attention, @smashercosmo. We should add this in v6, @chaance.
Actually, I think I may have spoken too soon. Just thinking about this some more...
The reason we needed a <NavLink isActive>
prop in v5 is because it was originally released in a world without React hooks, so you couldn't easily get access to the router's state. However, in v6 we have a whole bunch of hooks that should make it really easy to do whatever you need to do @smashercosmo.
For example, let's say you're building a product filter UI where you keep the value of the filter in the query string. And let's say you're selling shoes and you want to filter by brand. Your URLs might look something like /shoes?brand=nike
or /shoes?brand=puma
, and you could <Link>
to them.
Our <NavLink>
API is designed for navigation links, which tend to update the URL pathname. But in this case, the links to these URLs feel more like filters and less like navigation since they update the query string instead of the pathname. And since <NavLink>
is not able to match the search string as well, it doesn't really work for this use case.
However, v6 does give you the tools to build your own BrandLink
. In this case, you could use the useSearchParams
hook to access the current URLSearchParams
and do your own comparison to figure out if your link should be active or not. It could be something as simple as this:
import { useSearchParams, Link } from "react-router-dom";
function BrandLink({ brand, children }) {
let [searchParams] = useSearchParams();
// Use your own logic here to tell if the link is active or not...
let isActive = searchParams.get('brand') === brand;
return (
<Link
className={isActive ? 'active' : ''}
to={`/shoes?brand=${brand}`}
>
{children}
</Link>
);
}
// Then, use it like:
<BrandLink brand="nike">Nike</BrandLink>
<BrandLink brand="puma">Puma</BrandLink>
Does that make sense?
In general we are trying to steer people more towards using the tools we give them to solve their own problems instead of trying to create abstractions for solving every use case. For this specific case, I think you should already have the tools you need to get it done.
I see your point. But how about just passing 'match' object to style and className functions along with isActive flag?
I think the docs may require updating: NavLink
A <NavLink> is a special kind of <Link> that knows whether or not it is "active".
From what I can see in 6.0.2
at least, isActive
is available in both the className
and style
props if you provide a function.
<NavLink to="about" className={({ isActive }) => isActive ? : 'active' : 'inactive' }>
About
</NavLink>
Not 100% sure if it covers your usecase, but seems like it?
It would be really great if we could abstract the isActive
logic into its own hook and then expose it so that it can be used in other components.
How would I access the IsActive state inside a child component/element of the navlink? IOW: Say I want to change the classname of an icon inside of my NavLink based on its active/inactive state.
I think you should stop making API changes on small versions. This is a pain to keep track of, and looks like it is for those updating docs as well.
For example, You can also pass a function as children to customize the content of the <NavLink> component based on their active state, specially useful to change styles on internal elements.
(gathered from here).
This no longer works, only in style and classNames
This means that this code snippet doesn't work (gathered from the same page)
<li>
<NavLink to="tasks">
{({ isActive }) => (
<span
className={
isActive ? activeClassName : undefined
}
>
Tasks
</span>
)}
</NavLink>
</li>
Passing it on the children function doesn't work either (children={({ isActive }) => ().
For those reading, this is the easiest approach atm:
const location = useLocation();
const isActive = location.pathname === item.To;