Breadcrumbs and NextJS

Viewed 13642

I'd like to make a breadcrumb for my NextJS application, and I'd like to know if there is a good or practical way to do it?

I'd like to go through Ant Design, but the example of the component with React Router doesn't want to work for me.

I need your help please...

7 Answers

If you are using the Next.js routing system I suggest you have a look at nextjs-breadcrumbs, it is simple and effective.

Or if you prefer to write your own component use their code as reference.

import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import Link from 'next/link';

const convertBreadcrumb = string => {
  return string
    .replace(/-/g, ' ')
    .replace(/oe/g, 'ö')
    .replace(/ae/g, 'ä')
    .replace(/ue/g, 'ü')
    .toUpperCase();
};

const Breadcrumbs = () => {
  const router = useRouter();
  const [breadcrumbs, setBreadcrumbs] = useState(null);

  useEffect(() => {
    if (router) {
      const linkPath = router.asPath.split('/');
      linkPath.shift();

      const pathArray = linkPath.map((path, i) => {
        return { breadcrumb: path, href: '/' + linkPath.slice(0, i + 1).join('/') };
      });

      setBreadcrumbs(pathArray);
    }
  }, [router]);

  if (!breadcrumbs) {
    return null;
  }

  return (
    <nav aria-label="breadcrumbs">
      <ol className="breadcrumb">
        <li>
          <a href="/">HOME</a>
        </li>
        {breadcrumbs.map((breadcrumb, i) => {
          return (
            <li key={breadcrumb.href}>
              <Link href={breadcrumb.href}>
                <a>
                  {convertBreadcrumb(breadcrumb.breadcrumb)}
                </a>
              </Link>
            </li>
          );
        })}
      </ol>
    </nav>
  );
};

export default Breadcrumbs;

This my approach to bread crumbs in nextjs, it uses the router.asPath to get the link to the crumb and the router.route to get the label if it exists in the Route2LabelMap.

pages/example.jsx

export default function Page() {
  return <>
    <BreadCrumbs />
    <h1>My Page</h1>
  </>
}

components/BreadCrumbs.jsx

import Link from "next/link";
import { useRouter } from "next/router";
import React from "react";

/*
interface BreadCrumb {
  route: string;
  label: string;
  link: string;
}
*/

const Route2LabelMap = {
  "/": "Home",
  "/profile/[username]/barrels": "Your Barrels",
  "/barrels": "Barrel List",
  "/barrels/[barrel_id]": "Barrel",
  "/barrels/[barrel_id]/settings": "Settings",
};

export function BreadCrumbs() {
  const router = useRouter();

  const [crumbs, setCrumbs] = React.useState([]);

  React.useEffect(() => {
    const segmentsPath = router.asPath.split("/");
    const segmentsRoute = router.route.split("/");
    const crumbLinks = CombineAccumulatively(segmentsPath);
    const crumbLabels = CombineAccumulatively(segmentsRoute);

    const crumbs = crumbLinks.map((link, index) => {
      const route = crumbLabels[index];
      const crumb = {
        link: link,
        route: route,
        label: Route2LabelMap[route] || route,
      };
      return crumb;
    });
    setCrumbs(crumbs);

    console.log({
      router,
      segmentsPath,
      segmentsRoute,
      crumbLinks,
      crumbLabels,
      crumbs,
    });
  }, [router.route]);

  return (
    <div className="w-full flex gap-1">
      {crumbs.map((c, i) => {
        return (
          <div className="flex items-center gap-1" key={i}>
            {(i > 0) ? <div>{'>'}</div> : null}
            <div className={(i == (crumbs.length - 1) ? 'bg-blue-300 ' : 'bg-gray-300 ') + " px-2 py-1 rounded-xl"}>
              <Link href={c.link}>
                <a>{c.label}</a>
              </Link>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function CombineAccumulatively(segments) {
  /* 
  when segments = ['1','2','3']
  returns ['1','1/2','1/2/3']
  */
  const links = segments.reduce((acc, cur, curIndex) => {
    const last = curIndex > 1 ? acc[curIndex - 1] : "";
    const newPath = last + "/" + cur;
    acc.push(newPath);
    return acc;
  }, []);
  return links;
}

Rendered BreadCrumbs

In this case, either the route is shown, or the label is shown if it exists in the Route2LabelMap variable, like "Your Barrels" does.

enter image description here

    const router = useRouter()
    const linkPath = router.asPath.split('/');
    linkPath.shift();

    const pathArray = linkPath.map((path, i) => {
        return { breadcrumb: path, href: '/' + linkPath.slice(0, i + 1).join('/') };
    });
    console.log(pathArray)

Installation

yarn add nextjs-antd-breadcrumbs

Usage

The component needs to be used within Next.js and won't work in plain React. It will always display a dynamic Breadcrumb navigation, based on the current path of the Next router.

import React from 'react';
import Breadcrumbs from 'nextjs-antd-breadcrumbs';

const Example = () => {
  return <Breadcrumbs rootLabel="Home" omitRootLabel={false}/>;
};

I thought of using nextjs breadcrumbs but i was not able to align its style with antd (Ant Desing). And wanted to use the default breadcrumb component that antd was providing, thus, return some own simple component which uses next js router and some chunk of js.

const router = useRouter();
let routes= router.route.split('/');
let str='';
let hlinks=[];
routes.forEach((i,index,arr)=>{
    if(i.charAt(0)=='[')
    {
        i=i.slice(1);
        i=i.slice(0, i.length - 1);
        console.log(i)
        arr[index]=router.query[i];
    }
    str=str+arr[index]+'/';
    hlinks.push(str);
});
console.log(hlinks);
console.log(routes);

You can utilize breadcrumb with Next JS as below:

import { Breadcrumb } from "antd";
import styles from "../styles/Home.module.scss";
import { useRouter } from "next/router";
import React from "react";
import Link from "next/link";
import { HomeOutlined } from "@ant-design/icons";

const BreadcrumbComp = () => {
  const router = useRouter();
  const value = router.asPath.toLowerCase().split("/");
  console.log(value);

  const path = value.map(
    (value) => value.charAt(0).toUpperCase() + value.substring(1).toLowerCase()
  );

  return (
    <div>
      <Breadcrumb className={styles.breadcrumb}>
        <Breadcrumb.Item>
          <Link href="/home">
            <HomeOutlined />
          </Link>
        </Breadcrumb.Item>
        <Breadcrumb.Item>{path[1] + " / " + path[2]}</Breadcrumb.Item>
      </Breadcrumb>
    </div>
  );
};

export default BreadcrumbComp;

You can implement Breadcrumbs on Next JS with Antd Design as below:

import { Breadcrumb } from "antd";
import styles from "../styles/Home.module.scss";
import { useRouter } from "next/router";
import React from "react";
import Link from "next/link";
import { HomeOutlined } from "@ant-design/icons";

const BreadcrumbComp = () => {
  const router = useRouter();
  const value = router.asPath.toLowerCase().split("/");
  console.log(value);

  const path = value.map(
    (value) => value.charAt(0).toUpperCase() + value.substring(1).toLowerCase()
  );

  return (
    <div>
      <Breadcrumb className={styles.breadcrumb}>
        <Breadcrumb.Item>
          <Link href="/home">
            <HomeOutlined />
          </Link>
        </Breadcrumb.Item>
        <Breadcrumb.Item>{path}</Breadcrumb.Item>
      </Breadcrumb>
    </div>
  );
};

export default BreadcrumbComp;

Related