DEV Community

Kenneth Mark
Kenneth Mark

Posted on

Implementing ActiveLink in Next.js and Chakra

I love Next.js, its speed, simplicity, tooling but one thing I hate about Next.js is its lack of a native active link component. Before Next.js, I was using create-react-app and react-router for most of my personal projects but once I switched over to Next.js one of my biggest pain point is that I have to pretty much write my own NavLink component every single time for every new project I made. There are a couple of tutorials and Stackoverflows on how to exactly solve this problem but none of them actually achieves the simplicity react-router offers out fo the box.

In react-router when you want to make use of an active link all you do is import the nav-link component and pass it an activeClassName prop and voila it works. Just like this:

<NavLink activeClassName='is-active' to='/about'>About</NavLink>
Enter fullscreen mode Exit fullscreen mode

Most of tutorials on how to implement the ActiveLink api looks like this

// adapted from https://stackoverflow.com/questions/53262263/target-active-link-when-the-route-is-active-in-next-js
import { useRouter } from 'next/router'
import PropTypes from 'prop-types'
import Link from 'next/link'
import React, { Children } from 'react'

const ActiveLink = ({ children, activeClassName, ...props }) => {
  const { asPath } = useRouter()
  const child = Children.only(children)
  const childClassName = child.props.className || ''

  const className =
    asPath === props.href || asPath === props.as
      ? `${childClassName} ${activeClassName}`.trim()
      : childClassName

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

export default ActiveLink
Enter fullscreen mode Exit fullscreen mode

The only issue with this API is that, it still acts like the normal Next.js link component which requires you to implement a link tag() as the child of the link component. But what if you are not using a library like ChakraUI. The implementation in Chakra looks like this:

import { Link as ChakraLink, LinkProps, useColorModeValue } from '@chakra-ui/react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React from 'react'

interface NavLinkProps extends LinkProps {
  children?: string | React.ReactNode
  to: string
  activeProps?: LinkProps
  _hover?: LinkProps
}

function NavLink({ to, activeProps, children, _hover, ...props }: NavLinkProps) {
  const router = useRouter()
  const isActive = router.pathname === to
  const color = useColorModeValue('black', 'selected')

  if (isActive) {
    return (
      <Link href={to}>
        <ChakraLink
          fontWeight='bold'
          {...props}
          {...activeProps}
          _hover={{ color: 'selected' }}
          color={color}>
          {children}
        </ChakraLink>
      </Link>
    )
  }

  return (
    <Link href={to}>
      <ChakraLink {...props} _hover={{ color: 'selected' }}>
        {children}
      </ChakraLink>
    </Link>
  )
}

export default NavLink
Enter fullscreen mode Exit fullscreen mode

And to use it:


<NavLink mr={4} to='/dashboard'>
   Dashboard
</NavLink>

<NavLink mr={4} to='/dashboard' activeProps={{fontWeight:'bold'}}>
    Dashboard
 </NavLink>
Enter fullscreen mode Exit fullscreen mode

Discussion (3)

Collapse
brandiqa profile image
Michael Wanyoike

There's a prop called _activeLink that you can use if you are using the NavLink component from React Router or Remix package. Basically it will work with any routing package or logic that adds the .active class to the active link.

Collapse
ciboga profile image
Marcos

This is the perfect solution to keep the Nextjs link functionality. Thank you very much for this!

Collapse
iamabdulazeez profile image
I-am-abdulazeez

Thank you, I actually had this issue some weeks ago.