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>
  );
}

© 2021-2024 Rexford Essilfie. All rights reserved.