Snippets
useIsActiveRoute
React hook + helpers to check if a route is active in a Next.js app
TS
import { useCallback } from "react";
import { useRouter } from "next/router";
/**
* Returns a function to check if a given route
* is currently active.
*/
export function useIsActiveRoute() {
const { pathname } = useRouter();
// const pathname = usePathname() // for 'app' directory
const isActiveRoute = useCallback(
(route: string, baseRoute = "/") =>
isMatchingRoute(route, pathname, baseRoute),
[pathname]
);
return isActiveRoute;
}
/**
* Checks if two routes match. Two routes are a match if the second
* is a sub-path of the first.
*
* Supports optional `baseRoute` to prevent matching on sub paths.
* Omitting the `baseRoute` on the root path (`'/'`) for example
* will cause all paths to match with it.
*
* @example
* isMatchingRoute("/", "/") // => true
* isMatchingRoute("/", "/projects", "/") // => false
* isMatchingRoute("/", "/projects", "") // => true
* */
function isMatchingRoute(route: string, otherRoute: string, baseRoute = "/") {
route = removePrefix(baseRoute, route);
otherRoute = removePrefix(baseRoute, otherRoute);
return route === "" ? route == otherRoute : otherRoute.startsWith(route);
}
/**
* Removes a prefix from a string.
*/
function removePrefix(prefix: string, str: string) {
return str.replace(new RegExp(`^${prefix}`), "");
}
import { useCallback } from "react";
import { useRouter } from "next/router";
/**
* Returns a function to check if a given route
* is currently active.
*/
export function useIsActiveRoute() {
const { pathname } = useRouter();
// const pathname = usePathname() // for 'app' directory
const isActiveRoute = useCallback(
(route: string, baseRoute = "/") =>
isMatchingRoute(route, pathname, baseRoute),
[pathname]
);
return isActiveRoute;
}
/**
* Checks if two routes match. Two routes are a match if the second
* is a sub-path of the first.
*
* Supports optional `baseRoute` to prevent matching on sub paths.
* Omitting the `baseRoute` on the root path (`'/'`) for example
* will cause all paths to match with it.
*
* @example
* isMatchingRoute("/", "/") // => true
* isMatchingRoute("/", "/projects", "/") // => false
* isMatchingRoute("/", "/projects", "") // => true
* */
function isMatchingRoute(route: string, otherRoute: string, baseRoute = "/") {
route = removePrefix(baseRoute, route);
otherRoute = removePrefix(baseRoute, otherRoute);
return route === "" ? route == otherRoute : otherRoute.startsWith(route);
}
/**
* Removes a prefix from a string.
*/
function removePrefix(prefix: string, str: string) {
return str.replace(new RegExp(`^${prefix}`), "");
}
Examples
NavBar.tsx
Example use in a navigation bar component
TSX
import React from "react";
import Link from "next/link";
import { useIsActiveRoute } from "./useIsActiveRoute";
export function NavBar() {
const isActiveRoute = useIsActiveRoute();
const links = [
{ name: "Home", href: "/" },
{ name: "Coding", href: "/coding" },
{ name: "Snippets", href: "/snippets" },
{ name: "Resume ⤴", href: "/resume" },
];
return (
<nav>
<ul>
{links.map((link) => {
const isLinkActive = isActiveRoute(link.href);
const style = isLinkActive ? { color: "purple" } : { color: "gray" };
return (
<li key={link.name} style={style}>
<Link href={link.href}>{link.name}</Link>
</li>
);
})}
</ul>
</nav>
);
}
import React from "react";
import Link from "next/link";
import { useIsActiveRoute } from "./useIsActiveRoute";
export function NavBar() {
const isActiveRoute = useIsActiveRoute();
const links = [
{ name: "Home", href: "/" },
{ name: "Coding", href: "/coding" },
{ name: "Snippets", href: "/snippets" },
{ name: "Resume ⤴", href: "/resume" },
];
return (
<nav>
<ul>
{links.map((link) => {
const isLinkActive = isActiveRoute(link.href);
const style = isLinkActive ? { color: "purple" } : { color: "gray" };
return (
<li key={link.name} style={style}>
<Link href={link.href}>{link.name}</Link>
</li>
);
})}
</ul>
</nav>
);
}