A semantic page navigation set with current-page feedback, generous targets, and small-screen labels.
Showing 21–30 of 128 reports
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationPrevious,
PaginationNext,
PaginationEllipsis,
} from "@/components/ballmac/pagination"
export default function PaginationDemo() {
return (
<div className="w-full max-w-md rounded-xl border bg-card p-5 text-center shadow-sm">
<p className="mb-4 text-sm text-muted-foreground">
Showing 21–30 of 128 reports
</p>
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="#page-2" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#page-1">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#page-3" isActive>
3
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#page-4">4</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href="#page-4" />
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/paginationInstall the dependencies.
$ pnpm add lucide-reactAdd the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/i18nCopy the source into your project.
components/ballmac/pagination.tsx// Ballmac UI: Pagination. https://ui.ballmac.com/components/pagination // Based on shadcn/ui Pagination (MIT, Copyright (c) 2023 shadcn), adding current-page contrast and compact overflow behavior. "use client" import * as React from "react" import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" import { cn } from "@/lib/utils" import { useMessages } from "@/lib/ballmac/i18n" type PaginationProps = React.ComponentProps<"nav"> & { /** Accessible name of the navigation landmark. */ "aria-label"?: string } function Pagination({ className, "aria-label": label, ...props }: PaginationProps) { const msg = useMessages() label ??= msg("pagination.label", "Pagination") return ( <nav data-slot="pagination" aria-label={label} className={cn("mx-auto flex w-full justify-center", className)} {...props} /> ) } function PaginationContent({ className, ...props }: React.ComponentProps<"ul">) { return ( <ul data-slot="pagination-content" className={cn("flex min-w-0 items-center gap-1", className)} {...props} /> ) } function PaginationItem(props: React.ComponentProps<"li">) { return <li data-slot="pagination-item" {...props} /> } type PaginationLinkProps = React.ComponentProps<"a"> & { /** Mark the current page for visual and screen reader feedback. */ isActive?: boolean } function PaginationLink({ className, isActive = false, ...props }: PaginationLinkProps) { return ( <a data-slot="pagination-link" aria-current={isActive ? "page" : undefined} className={cn( "focus-visible:ring-ring/50 inline-flex size-9 items-center justify-center rounded-md border border-transparent text-sm transition-[color,background-color,border-color] duration-150 outline-none hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px]", isActive && "border-border bg-accent font-semibold text-accent-foreground", className, )} {...props} /> ) } function PaginationPrevious({ className, children = "Previous", ...props }: React.ComponentProps<"a">) { const msg = useMessages() return ( <a data-slot="pagination-previous" aria-label={msg("pagination.previousPage", "Previous page")} className={cn( "focus-visible:ring-ring/50 inline-flex h-9 items-center gap-1 rounded-md px-2 text-sm outline-none hover:bg-accent focus-visible:ring-[3px]", className, )} {...props} > <ChevronLeft aria-hidden="true" className="size-4 rtl:rotate-180" /> <span className="hidden sm:inline">{children}</span> </a> ) } function PaginationNext({ className, children = "Next", ...props }: React.ComponentProps<"a">) { const msg = useMessages() return ( <a data-slot="pagination-next" aria-label={msg("pagination.nextPage", "Next page")} className={cn( "focus-visible:ring-ring/50 inline-flex h-9 items-center gap-1 rounded-md px-2 text-sm outline-none hover:bg-accent focus-visible:ring-[3px]", className, )} {...props} > <span className="hidden sm:inline">{children}</span> <ChevronRight aria-hidden="true" className="size-4 rtl:rotate-180" /> </a> ) } function PaginationEllipsis({ className, ...props }: React.ComponentProps<"span">) { const msg = useMessages() return ( <span data-slot="pagination-ellipsis" className={cn( "flex size-9 items-center justify-center text-muted-foreground [&>svg]:size-4", className, )} {...props} > <MoreHorizontal aria-hidden="true" /> <span className="sr-only">{msg("pagination.morePages", "More pages")}</span> </span> ) } export { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis, type PaginationProps, type PaginationLinkProps, }Update the import paths to match your project setup.
Usage
import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis } from "@/components/ballmac/pagination"The full example is in the Code tab above.
Examples
States and variants
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
} from "@/components/ballmac/pagination"
export default function PaginationStates() {
return (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationLink href="#page-1" isActive>
1
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#page-2">2</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#page-3">3</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href="#page-2" />
</PaginationItem>
</PaginationContent>
</Pagination>
)
}API reference
<Pagination>
| Prop | Type | Default |
|---|---|---|
"aria-label"Accessible name of the navigation landmark. | string | — |
<PaginationLink>
| Prop | Type | Default |
|---|---|---|
isActiveMark the current page for visual and screen reader feedback. | boolean | false |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Tab | moves between page links |
| Enter | opens the focused page |
Use with AI
A semantic page navigation set with current-page feedback, generous targets, and small-screen labels. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Pagination (@ballmac/pagination) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Navigate a large result set by page
- Show a current page and neighboring destinations
Not for
- Use infinite scroll when discrete pages are unnecessary
Registry JSON: https://ui.ballmac.com/r/pagination.json
Credits
Based on shadcn/ui Pagination, adapted by Ballmac. Free to use in personal and commercial projects.
- npm
- lucide-react
- Registry
- @ballmac/i18nshadcn/utils
Pairs well with
Button
A button with six variants, three sizes, a pill shape and a built-in loading state. buttonVariants() styles links the same way.
Animated Tabs
Radix Tabs with a pill or underline indicator that slides between triggers on a spring, and panels that fade in. Keeps full keyboard support and ARIA; controlled or uncontrolled.
Back To Top
A floating button that appears after scrolling, draws a progress ring, scrolls smoothly (instantly under reduced motion) and moves focus to the content.
Breadcrumb
A compact navigation trail with truncation, semantic current page, and visible keyboard focus.