shadimage
avertra-ui
uibreadcrumbsource: ok · hit

Breadcrumb

A breadcrumb navigation component.

npx shadcn add "https://micomponents.micustomer.io/r/breadcrumb.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Files1 · source cached 10/9/2026, 8:47:38 AM

src/components/ui/breadcrumb.tsx
"use client";

import * as React from "react";
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "../../app/primitives/breadcrumb";

import { CaretRight } from "phosphor-react";

interface BreadcrumbProps {
  path: string;
  homeElement?: React.ReactNode;
  containerClasses?: string;
  listClasses?: string;
  activeItemClasses?: string;
  language?: string;
  icons?: {
    [segment: string]: React.ReactNode;
  };
  spliteStyle?: "default" | "arrow";
}

export const DynamicBreadcrumb: React.FC<BreadcrumbProps> = ({
  path,
  homeElement,
  containerClasses,
  listClasses,
  activeItemClasses,
  language = "en",
  icons = {},
  spliteStyle = "default",
}) => {
  const pathNames = path
    .split("/")
    .filter((segment) => segment !== "dashboard" && segment.length > 0);

  console.log({ path, pathNames });
  const getHref = (index: number, paths: string[]) =>
    `/${language}/${paths
      .slice(0, index + 1)
      .join(
        spliteStyle === "default" ? "/" : spliteStyle === "arrow" ? ">" : "/",
      )}`;

  let displayPaths: {
    name: string;
    href: string;
    isLast: boolean;
    isEllipsis?: boolean;
  }[] = [];

  const total = pathNames.length;

  if (total <= 3) {
    displayPaths = pathNames.map((name, index) => ({
      name,
      href: getHref(index, pathNames),
      isLast: index === total - 1,
    }));
  } else {
    displayPaths = [
      { name: pathNames[0], href: getHref(0, pathNames), isLast: false },
      { name: "...", href: "", isLast: false, isEllipsis: true },
      {
        name: pathNames[total - 2],
        href: getHref(total - 2, pathNames),
        isLast: false,
      },
      {
        name: pathNames[total - 1],
        href: getHref(total - 1, pathNames),
        isLast: true,
      },
    ];
  }

  const defaultHomeElement = (
    <BreadcrumbLink
      className="text-secondary-foreground"
      href={`/${language}`}
      aria-label="Dashboard"
    >
      <span className="flex items-center gap-1">
        {icons?.["dashboard"] && (
          <span className="shrink-0">{icons["dashboard"]}</span>
        )}
        Dashboard
      </span>
    </BreadcrumbLink>
  );

  return (
    <Breadcrumb className={containerClasses}>
      <BreadcrumbList className={listClasses}>
        <BreadcrumbItem>{homeElement ?? defaultHomeElement}</BreadcrumbItem>

        {displayPaths.length > 0 && (
          <BreadcrumbSeparator>
            {spliteStyle === "arrow" ? (
              <CaretRight size={16} className="text-muted-foreground" />
            ) : (
              spliteStyle === "default" && (
                <span className="text-muted-foreground">/</span>
              )
            )}
          </BreadcrumbSeparator>
        )}

        {displayPaths.map((segment, index) => (
          <React.Fragment key={index}>
            <BreadcrumbItem className="capitalize">
              {segment.isEllipsis ? (
                <BreadcrumbPage className="text-muted-foreground">
                  ...
                </BreadcrumbPage>
              ) : segment.isLast ? (
                <BreadcrumbPage className={activeItemClasses}>
                  <span className="flex items-center gap-1">
                    {icons?.[segment.name] && (
                      <span className="shrink-0">{icons[segment.name]}</span>
                    )}
                    {segment.name.replace(/-/g, " ")}
                  </span>
                </BreadcrumbPage>
              ) : (
                <BreadcrumbLink
                  href={segment.href}
                  className="text-muted-foreground"
                >
                  <span className="flex items-center gap-1">
                    {icons?.[segment.name] && (
                      <span className="shrink-0">{icons[segment.name]}</span>
                    )}
                    {segment.name.replace(/-/g, " ")}
                  </span>
                </BreadcrumbLink>
              )}
            </BreadcrumbItem>

            {index < displayPaths.length - 1 && (
              <BreadcrumbSeparator>
                {spliteStyle === "arrow" ? (
                  <CaretRight size={16} className="text-muted-foreground" />
                ) : (
                  spliteStyle === "default" && (
                    <span className="text-muted-foreground">/</span>
                  )
                )}
              </BreadcrumbSeparator>
            )}
          </React.Fragment>
        ))}
      </BreadcrumbList>
    </Breadcrumb>
  );
};

More from avertra-ui

Browse all components in this library →