Search code examples
javascriptreactjsreact-routerreact-router-dom

How to highlight active (variable) link in ReactJs?


I know how to 'highlight' an active link in React.js, I do it like this:

<Link
  className={splitted[1] === 'Details'
    ? "bg-red-800 rounded-full hover:text-white"
    : "hover:bg-blue-800 rounded-full hover:text-white"
  }
  key={'xxx'}
  to={`/Details/${id}`}
>
  This is my link            
</Link>

(ok, maybe a bit overkill, but it works)

But I have problems doing the same with a bunch of links that are rendered via a mapping. I try to do the same but it doesn't work at all. What is wrong in the code below?

const renderedLinks = links.map((link) => {
  return (
    <Link
      className={splitted[1] === `${link.path}`
        ? "bg-red-800 rounded-full hover:text-white"
        : "hover:bg-blue-800 rounded-full hover:text-white"
      }
      key={link.label}
      to={link.path}
    >
      {link.label}
    </Link>
  )
});

So, how do I get the variable link.path into this?

Links are generated by this:

const links = [
  { label: 'Details', path: '/Details' },
  ...
]

And splitted is is the first path of the pathname obtained by useLocation.

I am using react-router-dom@6.


Solution

  • Instead of the Link component it would be better to use the NavLink component as it has logic baked-in to handle matching an active link to the current URL path. Use the className callback to access the isActive prop and conditionally apply the appropriate CSS classes.

    Example:

    <Link
      key={'xxx'}
      to={`/Details/${id}`}
      className={({ isActive }) => 
        [
          "rounded-full hover:text-white",
          isActive ? "bg-red-800" : "hover:bg-blue-800"
        ].join(" ")
      }
    >
      This is my link            
    </Link>
    
    const renderedLinks = links.map((link) => (
      <Link
        key={link.path}
        to={link.path}
        className={({ isActive }) => 
          [
            "rounded-full hover:text-white",
            isActive ? "bg-red-800" : "hover:bg-blue-800"
          ].join(" ")
        }
      >
        {link.label}            
      </Link>
    ));
    

    If you render links to both parent and child/descendent paths, i.e. "/details" and "/details/specificDetails" then you can also specify the end prop on the NavLink so the active link is matched by the end of the path.

    If the end prop is used, it will ensure this component isn't matched as "active" when its descendant paths are matched. For example, to render a link that is only active at the website root and not any other URLs, you can use:

    <NavLink to="/" end>
      Home
    </NavLink>
    

    Applied to your code:

    const renderedLinks = links.map((link) => (
      <Link
        key={link.path}
        to={link.path}
        className={({ isActive }) => 
          [
            "rounded-full hover:text-white",
            isActive ? "bg-red-800" : "hover:bg-blue-800"
          ].join(" ")
        }
        end // <--
      >
        {link.label}            
      </Link>
    ));