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.
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.
Here is a solution that also works if URL-parameters are present and checks if a sub-page is active. Based on the answers by Darryl RN and Saman Mohamadi
It works as a drop-in replacement for the NextJS link component and adds the classes "active" and "active-sub" if the route or the route of a subpage is active.
Create a file called Link.js or whatever you like:
import { withRouter } from "next/router";
import Link from "next/link";
import React, { Children } from "react";
export default withRouter(({ router, children, as, href, activeClassName, activeSubClassName, ...rest }) => {
const child = Children.only(children);
const childClassName = child.props.className || "";
// remove URL parameters
const sanitizedPath = router.asPath.split("#")[0].split("?")[0];
// activeClassName and activeSubClassName are optional and default to "active" and "active-sub"
const activeClass = activeClassName || "active";
const activeSubClass = activeSubClassName || "active-sub";
// remove trailing slash if present
href = href && href !== "/" && href.endsWith("/") ? href.slice(0, -1) : href;
as = as && as !== "/" && as.endsWith("/") ? as.slice(0, -1) : as;
// check if the link or a sub-page is active and return the according class name
const activityClassName = sanitizedPath === href || sanitizedPath === as ? activeClass : sanitizedPath.startsWith(href + "/") || sanitizedPath.startsWith(as + "/") ? activeSubClass : "";
// combine the child class names with the activity class name
const className = `${childClassName} ${activityClassName}`.trim();
return (
<Link href={href} as={as} {...rest}>
{React.cloneElement(child, {
className: className || null,
})}
</Link>
);
});
import it in your files via
import Link from "./Link.js";
or with any name you like
import ActiveLink from "./Link.js";
and use it as you would use the NextJS "Link" component (next/link):
import { UrlObject } from "url";
interface ActiveLinkProps {
activeClassName?: string;
href: string | UrlObject;
}
// children is the <a>, prop is the "href"
const ActiveLink: React.FC<ActiveLinkProps> = ({ children, ...props }) => {
const router = useRouter();
// this will make sure i m passing only one child so i will access the its props easily
const child = Children.only(children) as React.ReactElement;
let className = child.props ? child.props.className : "";
if (router.asPath === props.href && props.activeClassName) {
className = `${className} ${props.activeClassName}`;
}
delete props.activeClassName;
return (
<Link href={props.href}>{React.cloneElement(child, { className })}</Link>
);
};
const router = useRouter();
const path = router.asPath.split("?")[0]; // (remove query string and use asPath since dynamic slug was rendering as "[slug]" using pathname)