ChatGPT解决这个技术问题 Extra ChatGPT

How do I add an active class to a Link from React Router?

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?


J
Jon Winstanley

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>

contentTypes in NavLink component should be static property not an instance property. Please correct.
Why not use activeStyle or activeClassName exposed by react-router. There is some documentation around it.
It appears that this no longer works as of react-router v4.0.0, as router.isActive no longer exists. See: github.com/react-bootstrap/react-router-bootstrap/issues/186
With react-router 3.0.0 , got TypeError: Cannot read property 'location' of undefined
Yes it should be the accepted answer. It worked beautifully for me! However, there is more to the config than just these steps. For me, it just made sure that my components in my nav were wrapped with this. There were other steps that made this work in the grand scheme of things. I will share them here a bit later when I have a moment.
T
Tamer Shlash

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>

This is on the wrapper to the link.
This outputs an anchor tag. The OP wants something that will output a list item.
Is this really working? I tried same but it's not working for me.
F
Fahad

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. Home Store About Us Style your class in the css by the property active. .active{ color:#fcfcfc; }


this works, thanks, found a solution to turn off the Home always being active also: stackoverflow.com/questions/51214763/…
r
resurrecteds

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
B
BenWurth

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
B
BaiJiFeiLong

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>

B
BenWurth

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.


s
sangeeth96

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.


u
user9944169

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


P
Prasanna Jathan

To set class on the active navigation element

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

&

<NavLink to="/Home" activeClassName="active">Home</NavLink>


D
Deepak kumar debug

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;


Throws error: Error: Invariant failed: You should not use <NavLink> outside a <Router>
B
BenWurth

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 ;)


B
Brent Washburne

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

I
Ismoil Shokirov

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 (

// or "" {item.text}
) }; const showItems = () => { // print elements return items.map((item, i) => { return element(item, i) }) }; return (
{showItems()} // print all the links we created in list
) }; export default withRouter(SidenavItems);


Hello! While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.
Hi! I have added some comments
S
Stephen Jenkins

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


M
Mehmet Recep Yildiz

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


Thanks, this works! Maybe you should consider add ```js to you codesample for colorize
Thanks for the colorization suggestion. It also worked :) Generally, stackoverflow colorizes on its own. It seems that for the jsx we need to add some hints for it :)
C
Community

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 ---


is it possible to match against 2 paths? So for example if one of the paths is true set the class to active Something like this: ${pathname.match('/protfolio' || /anotherpath)
@LyubomirIvanovValchev you can use logical operator like as: (${pathname.match('/protfolio') || ${pathname.match("/anotherpath") )? 'active-url' : ' '
can you suggest a better way if we have more path on single tab
j
justarandomguy

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


S
Subhadip Majumder

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(

  • MAIN NAVIGATION
  • Dashboard
  • Email Lists
  • Buy Verifications
) } } export default withRouter(NavBar);

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


E
Ernest

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:

will have unnecessary is-active class (as you only need it for

  • ), or you can remove this class in the callback func.

    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


  • A
    Anjana Parghi
    <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>
    

    Some explanation would make this a better answer.
    n
    nick

    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>
    
    </>
    )
    }
    

    P
    Peter Csala
    <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 :)


    m
    masterscode

    react-router-dom@v6

    import { NavLink } from "react-router-dom";
    
    <NavLink className = {isActive => isActive? 'active-css-class': 'general-css-class'}>link</NavLink>
    

    See "Explaining entirely code-based answers". While this might be technically correct, it doesn't explain why it solves the problem or should be the selected answer. We should educate along with helping solve the problem.
    K
    KARTHIKEYAN.A

    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>
        </>)
    }
    

    A
    Abhishek Dutt

    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>
    
    

    This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review
    import { NavLink } from "react-router-dom";
    @AbhishekDutt i hope this will help..
    D
    Das_Geek

    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.


    Dude, it is wrong to answer to the question in React with JQuery approach