I've created a bootstrap-style sidebar using Link
. Here is a snippet of my code:
<ul className="sidebar-menu">
<li className="header">MAIN NAVIGATION</li>
<li><Link to="dashboard"><i className="fa fa-dashboard"></i> <span>Dashboard</span></Link></li>
<li><Link to="email_lists"><i className="fa fa-envelope-o"></i> <span>Email Lists</span></Link></li>
<li><Link to="billing"><i className="fa fa-credit-card"></i> <span>Buy Verifications</span></Link></li>
</ul>
I want to set the class for the active path to active
on the wrapping element <li>
. I see there are other solutions out there that show how to do this like Conditionally set active class on menu using react router current route, however I don't think that it's the best way to set an active class on a wrapper to a Link
.
I also found https://github.com/insin/react-router-active-component but it feels like it is unnecessary.
In React Router, is this possible or do I need to use an external solution?
On the Link component you can now add activeClassName or set activeStyle.
These allow you to easily add styles to the currently active link.
Previously, you could create a custom component that works like a wrapper to Link with the following logic.
In a file called nav_link.js
import React from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
class NavLink extends React.Component {
render() {
var isActive = this.context.router.route.location.pathname === this.props.to;
var className = isActive ? 'active' : '';
return(
<Link className={className} {...this.props}>
{this.props.children}
</Link>
);
}
}
NavLink.contextTypes = {
router: PropTypes.object
};
export default NavLink;
And use it as given below in your component:
...
import NavLink from "./nav_link";
.....
<nav>
<ul className="nav nav-pills pull-right">
<NavLink to="/">
<i className="glyphicon glyphicon-home"></i> <span>Home</span>
</NavLink>
<NavLink to="about">
<i className="glyphicon glyphicon-camera"></i> <span>About</span>
</NavLink>
</ul>
</nav>
React-Router V4 comes with a NavLink component out of the box
To use, simply set the activeClassName
attribute to the class you have appropriately styled, or directly set activeStyle
to the styles you want. See the docs for more details.
<NavLink
to="/hello"
activeClassName="active"
>Hello</NavLink>
For me what worked has is using NavLink as it has this active class property.
First import it import { NavLink } from 'react-router-dom';
Use an activeClassName to get the active class property.
This is my way, using location from props. I don't know but history.isActive got undefined for me
export default class Navbar extends React.Component {
render(){
const { location } = this.props;
const homeClass = location.pathname === "/" ? "active" : "";
const aboutClass = location.pathname.match(/^\/about/) ? "active" : "";
const contactClass = location.pathname.match(/^\/contact/) ? "active" : "";
return (
<div>
<ul className="nav navbar-nav navbar-right">
<li className={homeClass}><Link to="/">Home</Link></li>
<li className={aboutClass}><Link to="about" activeClassName="active">About</Link></li>
<li className={contactClass}><Link to="contact" activeClassName="active">Contact</Link></li>
</ul>
</div>
);}}
<li class={homeClass}> ... </li>
change class => className
Answer updated with ES6:
import React, { Component } from 'react';
import { Link } from 'react-router'
class NavLink extends Component {
render() {
let isActive = this.context.router.isActive(this.props.to, true);
let className = isActive ? "active" : "";
return (
<li className={className}>
<Link {...this.props}/>
</li>
);
}
}
NavLink.contextTypes = {
router: React.PropTypes.object
};
export default NavLink;
Then use it as described above.
this.context.route
was undefined me for (newer version?), you can make work using withRouter
function which passes router in params
Just use NavLink
rather than Link
. It will add .active
class automatically.
<Nav className="mr-auto">
<Nav.Link as={NavLink} to="/home">Home</Nav.Link>
<Nav.Link as={NavLink} to="/users">Users</Nav.Link>
</Nav>
I didn't like the idea of creating a custom component, because if you have a different wrapping element you would have to create another custom component etc. Also, it is just overkill. So I just did it with css and activeClassName:
<li className="link-wrapper"> <!-- add a class to the wrapper -->
<Link to="something" activeClassName="active">Something</Link>
</li>
And then just add some css:
li.link-wrapper > a.active {
display: block;
width: 100%;
height:100%;
color: white;
background-color: blue;
}
Technically this doesn't style the li, but it makes the anchor fill the li and styles it.
With react-router-dom@4.3.1
(though any 4.x.x
should do I guess), we can use the withRouter
HOC to accomplish this. For example, I want to implement the Bootstrap navbar and since it requires a class of active
on <li class="nav-item">
and not on the anchor tag, I made a new component called NavItem
to encapsulate a single li.nav-item
. The implementation is as follows:
import React from "react";
import { Link, withRouter } from "react-router-dom";
const NavItem = ({ isActive, to, label }) => {
let classes = ["nav-item"];
if (isActive) classes.push("active");
return (
<li className={classes.join(" ")}>
<Link className="nav-link" to={to}>
{label}
</Link>
</li>
);
};
export default withRouter(({ location, ...props }) => {
const isActive = location.pathname === props.to;
console.log(location.pathname, props.to);
return <NavItem {...props} isActive={isActive} />;
});
As you can see, NavItem
is just a stateless functional component which expects an isActive
prop to determine whether active
class should be added. Now, to update this prop as the location changes, we can make use of the withRouter
HOC. You can pass any component to this function and it'll give it { match, location, history }
objects in its props along with the ones you pass down. Here, I am creating a functional component inline which receives these objects and determines whether the current link is the active one using the location.pathname
property. This'll give us a Boolean
and we can return the NavItem
along with isActive
set to the value we computed using location.pathname
.
A working example of this can be found here. Please let me know if there's an easier way to do this.
As of react-router-dom@4.3.1, we can just easily use the NavLink with activeClassName instead of Link. Example:
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
class NavBar extends Component {
render() {
return (
<div className="navbar">
<ul>
<li><NavLink to='/1' activeClassName="active">1</NavLink></li>
<li><NavLink to='/2' activeClassName="active">2</NavLink></li>
<li><NavLink to='/3' activeClassName="active">3</NavLink></li>
</ul>
</div>
);
}
}
Then in your CSS file:
.navbar li>.active {
font-weight: bold;
}
The NavLink will add your custom styling attributes to the rendered element based on the current URL.
Document is here
To set class on the active navigation element
import { NavLink } from 'react-router-dom';
&
<NavLink to="/Home" activeClassName="active">Home</NavLink>
Current React Router Version 5.2.0
activeStyle is a default css property of NavLink component which is imported from react-router-dom
So that we can write our own custom css to make it active.In this example i made background transparent and text to be bold.And I store it on a constant named isActive
import React from "react";
import { NavLink} from "react-router-dom";
function nav() {
const isActive = {
fontWeight: "bold",
backgroundColor: "rgba(255, 255, 255, 0.1)",
};
return (
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<NavLink className="nav-link" to="/Shop" activeStyle={isActive}>
Shop
</NavLink>
</li>
</ul>
);
export default nav;
Error: Invariant failed: You should not use <NavLink> outside a <Router>
You can actually replicate what is inside NavLink something like this
const NavLink = ( {
to,
exact,
children
} ) => {
const navLink = ({match}) => {
return (
<li class={{active: match}}>
<Link to={to}>
{children}
</Link>
</li>
)
}
return (
<Route
path={typeof to === 'object' ? to.pathname : to}
exact={exact}
strict={false}
children={navLink}
/>
)
}
just look into NavLink source code and remove parts you don't need ;)
Expanding on @BaiJiFeiLong's answer, add an active={}
property to the link:
<Nav.Link as={Link} to="/user" active={pathname.startsWith('/user')}>User</Nav.Link>
This will show the User
link as active when any path starts with '/user'. For this to update with each path change, include withRouter()
on the component:
import React from 'react'
import { Link, withRouter } from 'react-router-dom'
import Navbar from 'react-bootstrap/Navbar'
import Nav from 'react-bootstrap/Nav'
function Header(props) {
const pathname = props.location.pathname
return (
<Navbar variant="dark" expand="sm" bg="black">
<Navbar.Brand as={Link} to="/">
Brand name
</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<Nav.Link as={Link} to="/user" active={pathname.startsWith('/user')}>User</Nav.Link>
<Nav.Link as={Link} to="/about" active={pathname.startsWith('/about')}>About</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
)
}
export default withRouter(Header) // updates on every new page
import React from 'react'; import {withRouter, Link} from "react-router-dom"; const SidenavItems = (props) => { // create simple list of links const items = [ { type: "navItem", icon: "home", text: "Home", link: "/", restricted: false }, { type: "navItem", icon: "user-circle", text: "My Profile", link: "/user", restricted: false }, { type: "navItem", icon: "sign-in", text: "Login", link: "/login", restricted: false }, ]; const element = (item, i) => { // create elements (Links) // check if this is a current link on browser let active = ""; if (props.location.pathname === item.link) { active = "active"; } return (
React Router v6:
Source: Active NavLink Classes with React Router
You'll want to use the className
property which now accepts a function and passes an isActive
boolean property:
<NavLink
to="users"
className={({ isActive }) => (isActive ? 'active' : 'inactive')}
>
Users
</NavLink>
This is great for adding multiple classes too, and was a breaking change in v6 beta:
<NavLink
to="users"
className={({ isActive }) =>
isActive ? 'bg-green-500 font-bold' : 'bg-red-500 font-thin'
}
>
Users
</NavLink>
Read more and live demo: Active NavLink Classes with React Router
In react router Version 6, we're doing it by NavLink and setting its className property.
We're specifying a function which will be called on every route change here.
import { NavLink } from 'react-router-dom';
import classes from './MainHeader.module.css';
const MainHeader = () => {
return (
<header className={classes.header}>
<nav>
<ul>
<li>
<NavLink className={({isActive}) => isActive ? classes.active : ''} to='/welcome'>
Welcome
</NavLink>
</li>
<li>
<NavLink className={({isActive}) => isActive ? classes.active : ''} to='/products'>
Products
</NavLink>
</li>
</ul>
</nav>
</header>
);
};
export default MainHeader;
also, you can find a whole tutorial on it on this link
One of the way you can use it
When you are using the Functional component then follow the instruction here.
add a variable in your component
create an event change browser URL change/or other change
re-assign the current path (URL)
use javascript match function and set active or others
Use the above code here.
import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
const NavItems = () => {
let pathname = window.location.pathname;
useEffect(() => {
pathname = window.location.pathname;
}, [window.location.pathname]);
return (
<>
<li className="px-4">
<Link to="/home" className={`${pathname.match('/home') ? 'link-active' : ''}`}>Home</Link>
</li>
<li className="px-4">
<Link to="/about-me" className={`${pathname.match('/about-me') ? 'link-active' : ''}`}>About-me</Link>
</li>
<li className="px-4">
<Link to="/skill" className={`${pathname.match('/skill') ? 'link-active' : ''}`}>Skill</Link>
</li>
<li className="px-4">
<Link to="/protfolio" className={`${pathname.match('/protfolio') ? 'link-active' : ''}`}>Protfolio</Link>
</li>
<li className="pl-4">
<Link to="/contact" className={`${pathname.match('/contact') ? 'link-active' : ''}`}>Contact</Link>
</li>
</>
);
}
export default NavItems;
--- Thanks ---
The answer by Vijey has a bit of a problem when you're using react-redux for state management and some of the parent components are 'connected' to the redux store. The activeClassName is applied to Link only when the page is refreshed, and is not dynamically applied as the current route changes.
This is to do with the react-redux's connect function, as it suppresses context updates. To disable suppression of context updates, you can set pure: false
when calling the connect()
method like this:
//your component which has the custom NavLink as its child.
//(this component may be the any component from the list of
//parents and grandparents) eg., header
function mapStateToProps(state) {
return { someprops: state.someprops }
}
export default connect(mapStateToProps, null, null, {
pure: false
})(Header);
Check the issue here: reactjs#470
Check pure: false
documentation here: docs
Its very easy to do that, react-router-dom provides all.
import React from 'react'; import { matchPath, withRouter } from 'react-router'; class NavBar extends React.Component { render(){ return(
Wrapping You Navigation Component with withRouter() HOC will provide few props to your component: 1. match 2. history 3. location
here i used matchPath() method from react-router to compare the paths and decide if the 'li' tag should get "active" class name or not. and Im accessing the location from this.props.location.pathname.
changing the path name in props will happen when our link is clicked, and location props will get updated NavBar also get re-rendered and active style will get applied
Since router v4 I am using 'refs' for setting the parent active class:
<ul>
<li>
<NavLink
innerRef={setParentAsActive}
activeClassName="is-active"
to={link}
>
{text}
</NavLink>
</ul>
NavLink
's innerRef
prop accepts callback function, which will receive DOM node as an argument. You can use then any DOM manipulation possible, in this case simply set parent element (<li>
) to have the same class:
const setParentAsActive = node => {
if (node) {
node.parentNode.className = node.className;
}
};
Drawbacks:
if you change the element structure, f.e. wrap a tag inside a span, your callback will stop working, but it's possible to write more sofisticated DOM traverse function
you have to do some DOM manipulation
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand>
<Nav.Link as="{NavLink}" to="/">Brand Name</Nav.Link>
</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<nav className="me-auto">
<Nav.Link as="{NavLink}" to="/" exact>Home</Nav.Link>
<Nav.Link as="{NavLink}" to="/about">About</Nav.Link>
<Nav.Link as="{NavLink}" to="/contact">Contact</Nav.Link>
<NavDropdown title="Dropdown" id="basic-nav-dropdown">
<Nav.Link as="{NavLink}" to="/page1">Dropdown Link 1</Nav.Link>
<Nav.Link as="{NavLink}" to="/page2">Dropdown Link 2</Nav.Link>
<Nav.Link as="{NavLink}" to="/page3">Dropdown Link 3</Nav.Link>
</NavDropdown>
</nav>
</Navbar.Collapse>
</Container>
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
class NavBar extends Component {
render() {
return (
<div className="navbar">
<ul>
<li><NavLink to='/1' activeClassName="active">1</NavLink></li>
<li><NavLink to='/2' activeClassName="active">2</NavLink></li>
<li><NavLink to='/3' activeClassName="active">3</NavLink></li>
</ul>
</div>
);
}
}
this works for my...
Use React Hooks.
import React, { useState } from 'react';
export const Table = () => {
const [activeMenu, setActiveMenu] = useState('transaction');
return(
<>
<Link className="flex-1 mr-2">
<a
id="transaction"
className={
activeMenu == 'transaction'
? 'text-center block border border-blue-500 rounded py-2 px-4 bg-blue-500 hover:bg-blue-700 text-white'
: 'text-center block border border-white rounded hover:border-gray-200 text-blue-500 hover:bg-gray-200 py-2 px-4'
}
href="#"
onClick={() => {
setActiveMenu('transaction');
}}>
Recent Transactions
</a>
</Link>
<Link className="flex-1 mr-2">
<a
id="account"
className={
activeMenu == 'account'
? 'text-center block border border-blue-500 rounded py-2 px-4 bg-blue-500 hover:bg-blue-700 text-white'
: 'text-center block border border-white rounded hover:border-gray-200 text-blue-500 hover:bg-gray-200 py-2 px-4'
}
href="#"
onClick={() => {
setActiveMenu('account');
}}>
Account Statement
</a>
</LInk>
</>
)
}
<NavLink to='/' activeClassName='active'>
Home
</NavLink>
I just used NavLink
and activeClassName
without wrapping it up in any component or something but my Code still works :)
react-router-dom@v6
import { NavLink } from "react-router-dom";
<NavLink className = {isActive => isActive? 'active-css-class': 'general-css-class'}>link</NavLink>
We can use NavLink
tag to highlight active link using isActive
property
style:
const useStyles = makeStyles(theme => ({
active: {
borderBottom: '1px solid white',
color: 'yellow',
fontSize: '14px',
marginLeft: '16px',
textDecoration: 'none',
},}));
Component:
const classes = useStyles()
<NavLinks {...classes}/>
const NavLinks = classes => {
return (<>
<NavLink
to='/'
className={
useCallback(({isActive}) => isActive ? classes.active : classes.link)}
>
Home
</NavLink>
</>)
}
In react-router-dom documantion you use navlink instead of link. https://v5.reactrouter.com/web/api/NavLink
import { NavLink } from "react-router-dom";
<Nav>
<NavLink exact activeClassName="active--link" to="/" className="your class" > Home </NavLink>
<NavLink exact to="/classes" activeClassName="active--link" className="your class" > Classes </NavLink>
</Nav>
<NavLink to="/hello" activeClassName="active">Hello</NavLink>
Using Jquery for active link:
$(function(){
$('#nav a').filter(function() {
return this.href==location.href
})
.parent().addClass('active').siblings().removeClass('active')
$('#nav a').click(function(){
$(this).parent().addClass('active').siblings().removeClass('active')
})
});
Use Component life cycle method or document ready function as specified in Jquery.
Success story sharing
activeStyle
oractiveClassName
exposed byreact-router
. There is some documentation around it.router.isActive
no longer exists. See: github.com/react-bootstrap/react-router-bootstrap/issues/186TypeError: Cannot read property 'location' of undefined