My Brain
My Brain

Next.js Active Links

Next.js version of activeClassName support.

// ActiveLink.js

import { withRouter } from 'next/router';
import Link from 'next/link';
import React, { Children } from 'react';

const ActiveLink = ({ router, children, ...props }) => {
  const child = Children.only(children);

  let className = child.props.className || '';
  if (router.pathname === props.href && props.activeClassName) {
    className = `${className} ${props.activeClassName}`.trim();
  }

  delete props.activeClassName;

  return <Link {...props}>{React.cloneElement(child, { className })}</Link>;
};

export default withRouter(ActiveLink);

// Header.jsx

import Link from './Link'; // our version of link

export default () => (
  <header className="Header">
    <nav>
      <Link activeClassName="active" href="/">
        <a className="some-other-class">Home</a>
      </Link>
      <Link activeClassName="active" href="/about">
        <a>About</a>
      </Link>
      <Link activeClassName="active" href="/contact">
        <a>Contact</a>
      </Link>
    </nav>
  </header>
);
  • Awesome, thanks a lot! Here is my more minimal version for when the className does not have to be set using a prop:
import Link from "next/link";
import { withRouter } from "next/router";
import { Children } from "react";

const ActiveLink = withRouter(({ router, children, ...props }) => (
  <Link {...props}>
    {React.cloneElement(Children.only(children), {
      className: router.pathname === props.href ? `active` : null
    })}
  </Link>
));
  • In case someone only needs to match the first url segment, try this:
const ActiveLink = withRouter(({ router, children, ...props }) => (
  <Link {...props}>
    {React.cloneElement(Children.only(children), {
      className:
        `/${router.pathname.split("/")[1]}` === props.href ? `active` : null
    })}
  </Link>
));
  • Thanks a lot! Cleaned version of ActiveLink.
import { withRouter } from 'next/router';
import cx from 'classnames';
import Link from 'next/link';
import React, { Children } from 'react';

const ActiveLink = ({
  router,
  children,
  href,
  activeClassName,
  ...otherProps
}) => {
  const child = Children.only(children);
  const className = cx(child.props.className, {
    [activeClassName]: router.pathname === href && activeClassName
  });

  return (
    <Link href={href} {...otherProps}>
      {React.cloneElement(child, { className })}
    </Link>
  );
};
  • Here's a version using a Hook instead of a HOC.
import React from "react";
import Link from "next/link";
import { useRouter } from "next/router";

const ActiveLink = ({ children, ...props }) => {
  const router = useRouter();

  const child = React.Children.only(children);

  let className = child.props.className || "";
  if (router.pathname === props.href && props.activeClassName) {
    className = `${className} ${props.activeClassName}`.trim();
  }

  delete props.activeClassName;

  return <Link {...props}>{React.cloneElement(child, { className })}</Link>;
};
  • Here's a version using a Hook and styled-components:
const ActiveLink = ({ children, ...props }) => {
  const router = useRouter()
  const child = React.Children.only(children)
  return (
    <Link {...props}>
      {React.cloneElement(child, { active: router.pathname === props.href })}
    </Link>
  )
}

// Using example:
<ActiveLink href="/path" prefetch>
  <NavLink>Vikup auto</NavLink>
</ActiveLink>

// NavLink components something like this:

const NavLink = styled.a`
  color: ${({ active }) => active ? 'red' : 'black'}; 
`
  • A version with ES6 destructuring, proptypes and useRotuer hook:
import React from 'react';
import Link from 'next/link';
import PropTypes from 'prop-types';
import { useRouter } from 'next/router';

const ActiveLink = ({ href, activeClassName, children }) => {
  const router = useRouter();

  const child = React.Children.only(children);

  let className = child.props.className || '';
  if (router.pathname === href && activeClassName) {
    className = `${className} ${activeClassName}`.trim();
  }

  return <Link href={href}>{React.cloneElement(child, { className })}</Link>;
};

ActiveLink.propTypes = {
  href: PropTypes.string,
  activeClassName: PropTypes.string,
  children: PropTypes.node.isRequired
};
ActiveLink.defaultProps = {
  href: '',
  activeClassName: ''
};

export default ActiveLink;
  • I do it like this:

const ActiveLink: NextPage<Props> = ({ children, href }) => {
  const router = useRouter();

  return (
    <Link href={href} passHref>
      <Anchor active={router.pathname === href}>{children}</Anchor>
    </Link>
  );
};

const Anchor = styled.a<{ active: boolean }>`
  color: ${props => (props.active ? 'red' : 'blue')};
`;

// If full control is needed just replace Link all together, from their docs:

import { useRouter } from 'next/router'

function ActiveLink({ children, href }) {
  const router = useRouter()
  const style = {
    marginRight: 10,
    color: router.pathname === href ? 'red' : 'black',
  }

  const handleClick = e => {
    e.preventDefault()
    router.push(href)
  }

  return (
    <a href={href} onClick={handleClick} style={style}>
      {children}
    </a>
  )
}
  • If you need support for dynamic routes ([slug], [id], [...param], [[...slug]], etc.) in Next.js. The following will check if the href contains a dynamic route and then check if the "router.asPath" is equal to the "props.as". When matched the "activeClassName" will be added to the link.
import { useRouter } from "next/router";
import Link from "next/link";
import React, { Children } from "react";

const ActiveLink = ({ children, ...props }) => {
  const router = useRouter();
  const child = Children.only(children);
  let className = child.props.className || "";
  const isDynamicRoute = props.href.match(/^\/?\[{1,2}\.{0,3}[a-z]+\]{1,2}$/);

  if (
    router.pathname === props.href &&
    !isDynamicRoute &&
    props.activeClassName
  ) {
    className = `${className} ${props.activeClassName}`.trim();
  } else if (router.asPath === props.as && isDynamicRoute) {
    className = `${className} ${props.activeClassName}`.trim();
  }

  delete props.activeClassName;

  return <Link {...props}>{React.cloneElement(child, { className })}</Link>;
};

export default ActiveLink;
  • A version with ES6 destructuring, proptypes and useRotuer hook:
import React from 'react';
import Link from 'next/link';
import PropTypes from 'prop-types';
import { useRouter } from 'next/router';

const ActiveLink = ({ href, activeClassName, children }) => {
  const router = useRouter();

  const child = React.Children.only(children);

  let className = child.props.className || '';
  if (router.pathname === href && activeClassName) {
    className = `${className} ${activeClassName}`.trim();
  }

  return <Link href={href}>{React.cloneElement(child, { className })}</Link>;
};

ActiveLink.propTypes = {
  href: PropTypes.string,
  activeClassName: PropTypes.string,
  children: PropTypes.node.isRequired
};
ActiveLink.defaultProps = {
  href: '',
  activeClassName: ''
};

export default ActiveLink;
Thanks a lot, for use:

<NavLink href = "/path" activeClassName = "--active">
    <a className="classes">
        Home Page
    </a>
</NavLink>
  • Found on SO :
import Link from "next/link";
import { useRouter } from "next/router";

export const MyNav = () => {

  const router = useRouter();

  return (
    <ul>
      <li className={router.pathname == "/" ? "active" : ""}>
        <Link href="/">home</Link>
      </li>
      <li className={router.pathname == "/about" ? "active" : ""}>
        <Link href="/about">about</Link>
      </li>
    </ul>
  );
};
my code :

<Link href="/">
  <a className={router.pathname ==="/" ? styles.navlinkActive : styles.navlink}>
    Accueil
  </a>
</Link>

Backlinks