Next.js: New example: active route hook

Created on 23 May 2019  路  7Comments  路  Source: vercel/next.js

Feature request

Is your feature request related to a problem? Please describe.

The current <Link> component from next/link does not include an active prop when a link is clicked.

There is currently an example of how to implement active-class-name which allows styling of an active link by passing a className (and then styling it via style jsx). However, this might not work for users who are using UI frameworks such as bootstrap, or alternative CSS-in-JSS solutions like styled-components.

Describe the solution you'd like

I've adapted the ActiveLink.js file from the active-class-name` example to pass an active prop instead of a className. Here's the code:

# /components/Link.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 active = child.props.active || null;
  if (router.pathname === props.href) {
    active = true;
  }

  delete props.active;

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

export default withRouter(ActiveLink);

Then, when importing this component, e.g. in Nav.js, there is no need to include <Link activeClassName="active" href="/">. Instead, you can simply use <Link href="/"> and the active prop is automatically passed to whichever Link the user clicks on (e.g. <Link active href="/">

# /components/Nav.js

import Link from './Link'
import styled from 'styled-components'

const StyledLink = styled(Link)`
  text-decoration: none;
  padding: 10px;
  display: block;
  ${props =>
    props.active &&
    'background-color: #d9e9fb; color: #444; font-weight: 500;'}
`

export default () => (
  <nav>
    <ul>
      <li>
        <StyledLink href='/'>
          <a className='nav-link home-link'>Home</a>
        </StyledLink>
      </li>
      <li>
        <StyledLink href='/about'>
          <a className='nav-link'>About</a>
        </StyledLink>
      </li>
    </ul>
  </nav>
)
good first issue

Most helpful comment

i did something like this with typescript and tailwind css and it works fine :

import { useRouter } from 'next/router'
import Link from 'next/link'

type Props = {
  href: string
  linkName: string
  activeClassName?: string
} & typeof defaultProps

const defaultProps = {
  activeClassName: 'text-green font-600',
}

export const NavLink = ({ href, linkName, activeClassName }: Props) => {
  const router = useRouter()

  return (
    <Link href={href}>
      <a className={router.pathname === href ? activeClassName : null}>
        {linkName}
      </a>
    </Link>
  )
}

NavLink.defaultProps = defaultProps

All 7 comments

Updated the title to reflect that useRouter is now available.

Thanks @meuwka 馃挴

But this thing is not working if you route to nested links like www.example.com/blog/xyz . The link gets inactive in nested links

i did something like this with typescript and tailwind css and it works fine :

import { useRouter } from 'next/router'
import Link from 'next/link'

type Props = {
  href: string
  linkName: string
  activeClassName?: string
} & typeof defaultProps

const defaultProps = {
  activeClassName: 'text-green font-600',
}

export const NavLink = ({ href, linkName, activeClassName }: Props) => {
  const router = useRouter()

  return (
    <Link href={href}>
      <a className={router.pathname === href ? activeClassName : null}>
        {linkName}
      </a>
    </Link>
  )
}

NavLink.defaultProps = defaultProps

i did something like this with typescript and tailwind css and it works fine :

import { useRouter } from 'next/router'
import Link from 'next/link'

type Props = {
  href: string
  linkName: string
  activeClassName?: string
} & typeof defaultProps

const defaultProps = {
  activeClassName: 'text-green font-600',
}

export const NavLink = ({ href, linkName, activeClassName }: Props) => {
  const router = useRouter()

  return (
    <Link href={href}>
      <a className={router.pathname === href ? activeClassName : null}>
        {linkName}
      </a>
    </Link>
  )
}

NavLink.defaultProps = defaultProps

This works fine, tho I had to change router.pathname to router.asPath as I need the dynamic id too.

Was this page helpful?
0 / 5 - 0 ratings