Target Active Link when the route is active in Next.js
How to target the active Link in Next.js like they way we do it in React-Router-4? Meaning, give the active link a class when its route is active?
Solution 1:
A simple solution based on the useRouter
hook:
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>
);
};
You could also use router.asPath
instead of router.pathname
if you want to include the url query parameters. This can be useful if you want to handle anchor tags such as /#about
.
Solution 2:
First, you need to have a component called Link, with temporary attribute activeClassName
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 || ''
// pages/index.js will be matched via props.href
// pages/about.js will be matched via props.href
// pages/[slug].js will be matched via props.as
const className =
asPath === props.href || asPath === props.as
? `${childClassName} ${activeClassName}`.trim()
: childClassName
return (
<Link {...props}>
{React.cloneElement(child, {
className: className || null,
})}
</Link>
)
}
ActiveLink.propTypes = {
activeClassName: PropTypes.string.isRequired,
}
export default ActiveLink
Then have a navigation bar with created component Link and css selector :active
to differentiate between active and inactive link.
import ActiveLink from './ActiveLink'
const Nav = () => (
<nav>
<style jsx>{`
.nav-link {
text-decoration: none;
}
.active:after {
content: ' (current page)';
}
`}</style>
<ul className="nav">
<li>
<ActiveLink activeClassName="active" href="/">
<a className="nav-link">Home</a>
</ActiveLink>
</li>
<li>
<ActiveLink activeClassName="active" href="/about">
<a className="nav-link">About</a>
</ActiveLink>
</li>
<li>
<ActiveLink activeClassName="active" href="/[slug]" as="/dynamic-route">
<a className="nav-link">Dynamic Route</a>
</ActiveLink>
</li>
</ul>
</nav>
)
export default Nav
After that, you can implement the navigation bar to your page:
import Nav from '../components/Nav'
export default () => (
<div>
<Nav />
<p>Hello, I'm the home page</p>
</div>
)
The key of how does this work is located inside component Link, we compare the value of router.pathname
with attribute href
from the Link, if the value match the other then put specific className to make the link looks activated.
Reference: here
Solution 3:
Another minimal version which supports as
prop:
import Link from "next/link";
import {withRouter} from "next/router";
import {Children} from "react";
import React from "react";
export default withRouter(({router, children, as, href, ...rest}) => (
<Link {...rest} href={href} as={as}>
{React.cloneElement(Children.only(children), {
className: (router.asPath === href || router.asPath === as) ? `active` : null
})}
</Link>
));
Solution 4:
If you want to use an anchor Link try this version of @Rotareti 's code:
import Link from "next/link";
import { useRouter } from "next/router";
export const MyNav = () => {
const router = useRouter();
return (
<ul>
<li className={router.asPath == "/#about" ? "active" : ""}>
<Link href="#about">about</Link>
</li>
</ul>
);
}`;
Solution 5:
Typescript version:
import React from 'react'
import Link, { LinkProps } from 'next/link'
import { useRouter } from 'next/router'
export interface NavLinkProps extends LinkProps {
children: React.ReactElement
}
export function NavLink({ children, href, ...props }: NavLinkProps) {
const router = useRouter()
return (
<Link href={href} {...props}>
{router.pathname === href ? React.cloneElement(children, { 'data-active': true }) : children}
</Link>
)
}
Note that I'm not cloning the child unless necessary.