Breadcrumb and Pagination
Breadcrumb and Pagination: anatomy, keyboard behavior, state, and theming in Askr.
Example
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from '@askrjs/themes/components';
<Breadcrumb aria-label="Breadcrumb">
<BreadcrumbList>
<BreadcrumbItem><BreadcrumbLink href={projectsRoute}>Projects</BreadcrumbLink></BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem><BreadcrumbPage>{project.name}</BreadcrumbPage></BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>Purpose
Two small, unrelated-but-neighboring kits: Breadcrumb answers "where am I in the hierarchy," and Pagination answers "which page of results am I on." Both are plain markup/slot components — no routing awareness, no fetching, no page-count math baked in.
Install and import
tsx
import {
Breadcrumb, BreadcrumbList, BreadcrumbItem,
BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis,
} from '@askrjs/themes/breadcrumb';
import {
Pagination, PaginationContent, PaginationItem, PaginationLink,
PaginationPrevious, PaginationNext, PaginationEllipsis,
} from '@askrjs/themes/pagination';
Live examples
The example composes Breadcrumb down to the current page, with BreadcrumbPage marking the last crumb rather than a link — that is what stops a screen reader announcing the page you are already on as somewhere to go. Build the intermediate BreadcrumbLink destinations from route parameters so the trail stays correct when the route shape changes.
Anatomy
Breadcrumb renders <nav role="navigation">, BreadcrumbList an <ol>, BreadcrumbItem an <li>, and BreadcrumbLink an <a>; BreadcrumbPage is a <span> for the current, non-linked crumb. BreadcrumbSeparator and BreadcrumbEllipsis default their children to "/" and "..." respectively. Pagination mirrors the same shape: <nav role="navigation"> wrapping a PaginationContent <ul> of PaginationItem <li>s, with PaginationLink, PaginationPrevious (default label "Previous"), PaginationNext (default label "Next"), and PaginationEllipsis (default "...") as the individual <a>/<span> controls.
State model
Neither component knows which page or crumb is active on its own. For pagination, you pass active to PaginationLink yourself for the current page — it's the only prop that isn't purely cosmetic, and it drives both aria-current and a data-active attribute. For breadcrumbs, you simply render the last segment as BreadcrumbPage instead of BreadcrumbLink. Route state, current page number, and total-page math all live in your app code.
Keyboard and accessibility
Links are real <a> elements, so Tab and Enter work without any extra wiring. BreadcrumbPage sets aria-current="page" for you automatically; PaginationLink only sets it when you pass active. Both BreadcrumbSeparator/BreadcrumbEllipsis and PaginationEllipsis are marked aria-hidden="true" by default so screen readers skip the decorative slashes and dots rather than announcing them.
Styling and tokens
Each part exposes a matching data-slot: breadcrumb, breadcrumb-list, breadcrumb-item, breadcrumb-link, breadcrumb-page, breadcrumb-separator, breadcrumb-ellipsis on one side, and pagination, pagination-content, pagination-item, pagination-link, pagination-previous, pagination-next, pagination-ellipsis on the other. PaginationLink additionally exposes data-active="true" when it's the current page, which is the natural CSS hook for highlighting it.
API
All parts take the shared props — as, asChild, class, children, ref, plus passthrough attributes. The one addition worth knowing is PaginationLink's active?: boolean. Separator and ellipsis components accept a children override if you want something other than the default "/" or "..." glyph.
Edge cases
Long breadcrumb trails don't auto-collapse — if you want a truncated trail with BreadcrumbEllipsis standing in for hidden segments, you decide when and where to insert it. Same for pagination with many pages: there's no windowing logic shipped, so figuring out where PaginationEllipsis belongs is your app's job. If a page has more than one nav landmark (say, both a breadcrumb and the primary navigation menu), give each an aria-label yourself since neither component sets one automatically, and screen reader users rely on that label to tell them apart.
Related pages
Navbar and Navigation Menu for the primary nav bar these often sit under, Data Table for paginated tabular data, and Application Chrome for how breadcrumbs and pagination fit into a full page layout.