Implementing ActiveLink in Next.js and Chakra
October 04, 2024
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
prop and voila it works. Just like this:activeClassName
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-jsimport { 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(): childClassNamereturn (<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:
tsximport { 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.ReactNodeto: stringactiveProps?: LinkProps_hover?: LinkProps}function NavLink({ to, activeProps, children, _hover, ...props }: NavLinkProps) {const router = useRouter()const isActive = router.pathname === toconst color = useColorModeValue('black', 'selected')if (isActive) {return (<Link href={to}><ChakraLinkfontWeight='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! ✌️