Przejdź do treści

Breadcrumb

Pokazuje, gdzie w hierarchii znajduje się bieżąca strona, i jednym kliknięciem prowadzi na każdy wyższy poziom.

Kod źródłowyRejestr
import Link from "next/link"

import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"

export default function BreadcrumbDemo() {
  return (
    <Breadcrumb>
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink render={<Link href="/pl/docs" />}>Dokumentacja</BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbLink render={<Link href="/pl/docs/components" />}>
            Komponenty
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

Anatomia

  1. 1LinkPoziom nadrzędny. Używa tytułu strony docelowej.
  2. 2SeparatorDomyślnie dyskretny ukośnik; ukryty przed technologiami wspomagającymi.
  3. 3Bieżąca stronaOstatni element. Nie jest linkiem, ma atrybut aria-current="page".

Instalacja

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/breadcrumb.json

Użycie

import Link from "next/link"

import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
<Breadcrumb>
  <BreadcrumbList>
    <BreadcrumbItem>
      <BreadcrumbLink render={<Link href="/docs" />}>Docs</BreadcrumbLink>
    </BreadcrumbItem>
    <BreadcrumbSeparator />
    <BreadcrumbItem>
      <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
    </BreadcrumbItem>
  </BreadcrumbList>
</Breadcrumb>

BreadcrumbLink domyślnie renderuje <a>; przekaż link swojego routera przez render, aby zachować nawigację po stronie klienta. Ostatni element to BreadcrumbPage — bieżąca lokalizacja, która nie jest linkiem.

Przykłady

Domyślny

prfct oddziela poziomy subtelnym ukośnikiem. Czyta się go jak ścieżkę, zajmuje mniej miejsca niż strzałka i nie konkuruje z linkami.

import Link from "next/link"

import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"

export default function BreadcrumbDemo() {
  return (
    <Breadcrumb>
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink render={<Link href="/pl/docs" />}>Dokumentacja</BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbLink render={<Link href="/pl/docs/components" />}>
            Komponenty
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

Własny separator

Przekaż dowolną ikonę jako dziecko BreadcrumbSeparator. Strzałki pasują do ustawień i hierarchii plików, w których ścieżka oznacza schodzenie w głąb.

import { ChevronRightIcon } from "lucide-react"
import Link from "next/link"

import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"

export default function BreadcrumbCustomSeparator() {
  return (
    <Breadcrumb>
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink render={<Link href="/pl/docs" />}>
            Ustawienia
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator>
          <ChevronRightIcon />
        </BreadcrumbSeparator>
        <BreadcrumbItem>
          <BreadcrumbLink render={<Link href="/pl/docs" />}>
            Przestrzeń robocza
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator>
          <ChevronRightIcon />
        </BreadcrumbSeparator>
        <BreadcrumbItem>
          <BreadcrumbPage>Członkowie</BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

Zwinięta ścieżka

Gdy ścieżka ma więcej niż cztery poziomy, zostaw poziom główny i dwa ostatnie, a środek zwiń do menu rozwijanego Dropdown Menu ukrytego za wielokropkiem. Nic nie staje się nieosiągalne.

import Link from "next/link"

import {
  Breadcrumb,
  BreadcrumbEllipsis,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const hidden = [
  { title: "Inżynieria", href: "/pl/docs" },
  { title: "Platforma", href: "/pl/docs" },
  { title: "Design system", href: "/pl/docs" },
]

export default function BreadcrumbCollapsed() {
  return (
    <Breadcrumb>
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink render={<Link href="/pl/docs" />}>Acme</BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <DropdownMenu>
            <DropdownMenuTrigger
              aria-label="Pokaż ukrytą ścieżkę"
              className="rounded-md text-muted-foreground focus-ring transition-colors duration-fast hover:bg-accent hover:text-foreground data-popup-open:bg-accent"
            >
              <BreadcrumbEllipsis />
            </DropdownMenuTrigger>
            <DropdownMenuContent align="start" className="w-44">
              <DropdownMenuGroup>
                {hidden.map((item) => (
                  <DropdownMenuItem
                    key={item.title}
                    render={<Link href={item.href} />}
                  >
                    {item.title}
                  </DropdownMenuItem>
                ))}
              </DropdownMenuGroup>
            </DropdownMenuContent>
          </DropdownMenu>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbLink render={<Link href="/pl/docs" />}>
            Komponenty
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

Z ikonami

Ikona domu może zastąpić poziom główny, gdy nazwa produktu jest już widoczna w nagłówku — nadaj jej wtedy aria-label. Ikony w środku ścieżki powinny oznaczać rodzaj poziomu, a nie go ozdabiać.

import { FolderIcon, HouseIcon } from "lucide-react"
import Link from "next/link"

import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"

export default function BreadcrumbWithIcon() {
  return (
    <Breadcrumb>
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink
            render={<Link href="/pl" />}
            aria-label="Strona główna"
            className="flex items-center"
          >
            <HouseIcon className="size-4" />
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbLink
            render={<Link href="/pl/docs" />}
            className="flex items-center gap-1.5"
          >
            <FolderIcon className="size-4" />
            Projekty
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbPage>Strona marketingowa</BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

Przełącznik

W aplikacjach ostatni segment może jednocześnie przełączać między elementami tego samego poziomu — projektami w zespole, gałęziami w repozytorium. Wyzwalacz wygląda jak bieżąca strona, a menu oznacza aktywny element.

"use client"

import * as React from "react"
import { ChevronsUpDownIcon } from "lucide-react"
import Link from "next/link"

import { Badge } from "@/components/ui/badge"
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const projects = ["Strona marketingowa", "Aplikacja mobilna", "Design system"]

export default function BreadcrumbSwitcher() {
  const [current, setCurrent] = React.useState("Design system")

  return (
    <Breadcrumb>
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink
            render={<Link href="/pl/docs" />}
            className="font-medium text-foreground"
          >
            Acme
          </BreadcrumbLink>
          <Badge variant="secondary" size="sm">
            Pro
          </Badge>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <DropdownMenu>
            <DropdownMenuTrigger className="flex items-center gap-1.5 rounded-md px-1.5 py-1 font-medium text-foreground focus-ring transition-colors duration-fast hover:bg-accent data-popup-open:bg-accent">
              <span aria-current="page">{current}</span>
              <ChevronsUpDownIcon className="size-3.5 text-muted-foreground" />
            </DropdownMenuTrigger>
            <DropdownMenuContent align="start" className="w-52">
              <DropdownMenuGroup>
                <DropdownMenuLabel>Projekty</DropdownMenuLabel>
                <DropdownMenuRadioGroup
                  value={current}
                  onValueChange={setCurrent}
                >
                  {projects.map((project) => (
                    <DropdownMenuRadioItem key={project} value={project}>
                      {project}
                    </DropdownMenuRadioItem>
                  ))}
                </DropdownMenuRadioGroup>
              </DropdownMenuGroup>
            </DropdownMenuContent>
          </DropdownMenu>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

Wytyczne

Kiedy używać

  • W hierarchiach o głębokości co najmniej trzech poziomów — w dokumentacji, systemach plików, ustawieniach, kategoriach sklepów internetowych.
  • Gdy ktoś trafia głęboko w strukturę z wyszukiwarki lub udostępnionego linku i musi się zorientować, gdzie jest.

Kiedy nie używać

  • W płaskich serwisach z jednym lub dwoma poziomami — wystarczy tytuł strony.
  • Do pokazywania kolejnych kroków lub historii — ścieżka nawigacji opisuje położenie, a nie drogę, którą ktoś przeszedł. W procesach wieloetapowych użyj wskaźnika kroków.
  • Jako nawigacji głównej. Ścieżka nawigacji uzupełnia Sidebar lub Navigation Menu, ale ich nie zastępuje.

Treść

Każdy element używa tytułu strony docelowej, skróconego tylko w razie potrzeby. Nie linkuj bieżącej strony, nie powtarzaj jej tuż poniżej jako tytułu strony w innym brzmieniu i nie dodawaj separatora na końcu.

DobrzePoziomy nadrzędne są linkami, bieżąca strona to zwykły tekst.
ŹleSeparator na końcu i link do bieżącej strony nie prowadzą nigdzie dalej.

Umiejscowienie

Umieść ścieżkę nawigacji nad tytułem strony, wyrównaną z nim do lewej, w kolumnie treści. Na wąskich ekranach zwiń ją do poziomu głównego i nadrzędnego albo pokaż tylko link Wróć do poziomu wyżej.

Dostępność

  • Punkt orientacyjny. Breadcrumb renderuje <nav> z etykietą Ścieżka nawigacji (tłumaczoną przez LocaleProvider), więc użytkownicy czytników ekranu mogą przejść do niego z listy punktów orientacyjnych.
  • Kolejność. Elementy tworzą listę uporządkowaną (<ol>), która przekazuje hierarchię i liczbę poziomów.
  • Bieżąca strona. BreadcrumbPage renderuje <span> z aria-current="page", który oznacza go jako bieżącą lokalizację. To zwykły tekst, a nie link, więc nie przyjmuje fokusu.
  • Separatory mają aria-hidden, więc nie są odczytywane między poziomami.
  • Zwinięte poziomy wymagają wyzwalacza z dostępną nazwą, np. Pokaż ukrytą ścieżkę.
KlawiszDziałanie
Tab
Przenosi fokus kolejno po linkach. Bieżąca strona jest pomijana.
Enter
Otwiera link, na którym jest fokus.

Dokumentacja API

Punkt orientacyjny <nav>. Przyjmuje wszystkie propsy elementu <nav>; nadpisz aria-label, gdy strona ma więcej niż jedną ścieżkę nawigacji.

BreadcrumbList to <ol>, który układa ścieżkę i zawija ją na wąskich ekranach. BreadcrumbItem to <li>, który wyrównuje swoją zawartość — link, bieżącą stronę lub wyzwalacz menu.

PropTypDomyślnie
href

Adres docelowy linku.

stringBrak wartości domyślnej
render

Renderuje link frameworka, np. <Link href="/docs" />, z zachowaniem stylów prfct.

ReactElement | (props, state) => ReactElementBrak wartości domyślnej

Bieżąca lokalizacja. Renderuje <span> z aria-current="page".

PropTypDomyślnie
children

Zastępuje domyślny ukośnik, np. ikoną <ChevronRightIcon />.

ReactNode"/"

Znak ⋯ dla zwiniętych poziomów, odczytywany jako Więcej w języku ustawionym przez LocaleProvider. W wyzwalaczu nadaj samemu wyzwalaczowi nazwę, która mówi, co odsłania.