Implementing ActiveLink in Next.js and Chakra

Kenneth Coffie

October 12, 2021

3 min read

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:

tsx
<NavLink activeClassName='is-active' to='/about'>About</NavLink>

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

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

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:

tsx
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

And to use it:

jsx
<NavLink mr={4} to='/dashboard'>
Dashboard
</NavLink>
<NavLink mr={4} to='/dashboard' activeProps={{fontWeight:'bold'}}>
Dashboard
</NavLink>

Subscribe to my newsletter

You won't receive any spam! ✌️