October 9, 2023

React Router - NavLink in react-router-dom

In the post React Router - Link in react-router-dom we saw how you create navigational links in React routing. If you are using a framework like Bootstrap for styling with <Link> it works fine and you will have proper space between links, active link and non-active link styling will also be taken care of. But you may want more control over styling of links and that is possible with <NavLink> in React Router.

<NavLink> in React Router

Navlink is another way to create link which provides some extra functionalities. With NavLink you have the capability to know whether link is in "active" or "pending" state. This is useful when building a navigation menu where you want to highlight the currently selected menu item.

Navlink also provides useful context for assistive technology like screen readers.

How to get menu state information

The className prop used with NavLink takes a function which returns a css class name that should be added for styling. This function also receives an object which you can destructure to get isActive and isPending property.

These properties are of type Boolean. If current link is active then isActive has the value true otherwise value is false.

<NavLink
  to="/home"
  className={({ isActive, isPending }) =>
    isPending ? "pending" : isActive ? "active" : ""
  }
>
  Home
</NavLink>

An active class is also added to a <NavLink> component, when it is active, by default. So you can use CSS to style it.

Navigation menu using NavLink - React Example

Let's first create a navigation menu page

src\components\Routes\navigation.js

import { NavLink, Outlet } from "react-router-dom"
import "./navigation.css";

const Navigation = () => {
    return(
        <>
            <nav id="menu" className="navbar navbar-expand-lg bg-dark navbar-dark">
                <div className="container-fluid">
                    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                        <span className="navbar-toggler-icon"></span>
                    </button>

                    <div className="collapse navbar-collapse" id="navbarNav">
                        <ul className="navbar-nav">
                            <li className="nav-item">
                                <NavLink 
                                    to="/" 
                                    className={({ isActive }) => isActive ? "active" :""} 
                                    end
                                >
                                    Home
                                </NavLink>
                            </li>
                            <li className="nav-item">
                                <NavLink 
                                    to="/about"
                                    className={({ isActive }) => isActive ? "active" :""} 
                                >
                                    About
                                </NavLink>
                            </li>
                        </ul>
                    </div>
                </div>
            </nav>
            <Outlet />
        </>
    );
}

export default Navigation;

As you can see with NavLink there is a function to check if isActive property is true or not. If it is true then active class is added.

src\components\Routes\navigation.css

#menu a:link,
#menu a:visited {
    color: gray;
}
#menu a:hover {
    color: white;
}
#menu a.active {
    color:#ebecf0;
}

#menu a {
    text-decoration: none;
}

#menu ul {
    gap: 1rem;
}

Route Definition

src\components\Routes\route.js

import { createBrowserRouter } from "react-router-dom";
import About from "./about";
import Home from "./home";
import Navigation from "./navigation";

export const router = createBrowserRouter([
    {path: "/", element: <Navigation />,
     children: [
        {path: "/", element: <Home /> },
        {path: "/about", element: <About />}
     ]
    },        
])

Providing the routes

Provide the route definition to your application using the <RouteProvider> component.

import { RouterProvider } from 'react-router-dom';
import { router } from './components/Routes/route';

function App() {
  return <RouterProvider router={router}></RouterProvider>
}

export default App;

Components

src\components\Routes\home.js

const Home = () => {
    return (
        <>
            <h2>This is home page</h2>
        </>
    )
}

export default Home;

src\components\Routes\about.js

const About = () => {
    return <h2>This is a router demo</h2>
}
export default About;

When Home is clicked

NavLink in react-router-dom

When About is clicked

NavLink react example

end prop in NavLink

There is also an end prop used with NavLink to change the matching logic for the active and pending states to only match to the "end" of the NavLink's to path. Without using 'end' any URL that has path given in "to" within it will be matched even if path is longer. With 'end' it is an exact match, If the URL is longer than to, it will no longer be considered active.

For example, without using end; link <NavLink to="/tasks" /> will be matched for both '/tasks' and '/tasks/123' resulting in ‘isActive’ being true in both cases.

With this link where 'end' is also used- <NavLink to="/tasks" end /> isActive will be true only for '/tasks' not for '/tasks/123'

Using end with root route

As per react-router documentation you don't need to add end with root route so <NavLink to="/" end> is not required.

“<NavLink to="/"> is an exceptional case because every URL matches /. To avoid this matching every single route by default, it effectively ignores the end prop and only matches when you're at the root route.”

That's all for the topic React Router - NavLink in react-router-dom. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment