- Accordion
- Agent Thinking
- Alert
- Alert Dialog
- Announcement
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Checkbox
- Checkbox Card
- Code Block
- Collapsible
- Color Picker
- Combobox
- Command
- Composer
- Composer Loader
- Context Menu
- Data Grid
- Data Table
- Date Picker
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- File Upload
- Hover Card
- Input
- Input Group
- Input Otp
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Meter
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Period Switcher
- Popover
- Progress
- Questionnaire
- Radio Card
- Radio Group
- Resizable
- Reveal
- Scroll Area
- Search Field
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Social Button
- Sonner
- Spinner
- Status Dot
- Switch
- Switch Card
- Table
- Tabs
- Tag Group
- Textarea
- Theme Toggle
- Time Field
- Toggle
- Toggle Group
- Tooltip
- Trace
- Tree View
- Agent Creator 01
- Agent Limits Card 01
- Agent Progress 01
- AI Chat 01
- AI Image Generation 01
- AI Profile 01
- Alerts Card 01
- Auth Card 01
- Calendar 01
- Composer 01
- Composer Attachments 01
- Composer Panel 01
- Finance Dashboard 01
- Home Dashboard 01
- Marketing Dashboard 01
- Medical Profile 01
- Meeting Scheduler 01
- Multi Agent Chat 01
- Notification Center 01
- Patient Info Card 01
- Project Board 01
- Questionnaire 01
- Settings Modal 01
- Sidebar 01
- Stat Cards 01
- Task List 01
- Web Search 01
- Activity Rings Card 01
- Area Chart Card 01
- Bar List Card 01
- Combo Chart Card 01
- Contributions Card 01
- Earnings Chart Card 01
- Funnel Chart Card 01
- Heatmap Chart Card 01
- Line Chart Card 01
- Most Active Days Card 01
- Orders Chart Card 01
- Radar Chart Card 01
- Radial Chart Card 01
- Revenue Chart Card 01
- Sankey Chart Card 01
- Scatter Chart Card 01
- Sleep Score Card 01
- Stage Bars Card 01
- Steps Card 01
Installation
$ pnpm dlx shadcn@latest add @cobalt/pagination
Source
import { RiArrowLeftLine, RiArrowRightLine } from "@remixicon/react";
import { cn } from "cn";
import * as React from "react";
import { Button } from "@/components/ui/button";
const PAGINATION_DOTS = "dots";
function range(start: number, end: number) {
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
/**
* The page numbers to show: first, last, the current page and `siblingCount`
* neighbours on each side, with "dots" where pages are skipped. Changing the
* page re-windows the list, so the active cell moves as you click.
*/
function paginationRange(
current: number,
total: number,
siblingCount = 1
): (number | typeof PAGINATION_DOTS)[] {
if (siblingCount * 2 + 5 >= total) {
return range(1, total);
}
const left = Math.max(current - siblingCount, 1);
const right = Math.min(current + siblingCount, total);
const showLeftDots = left > 2;
const showRightDots = right < total - 2;
if (!showLeftDots && showRightDots) {
return [...range(1, 3 + 2 * siblingCount), PAGINATION_DOTS, total];
}
if (showLeftDots && !showRightDots) {
return [
1,
PAGINATION_DOTS,
...range(total - (2 + 2 * siblingCount), total),
];
}
return [1, PAGINATION_DOTS, ...range(left, right), PAGINATION_DOTS, total];
}
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
return (
<nav
aria-label="pagination"
data-slot="pagination"
className={cn(
"mx-auto flex w-full items-center justify-between gap-2",
className
)}
{...props}
/>
);
}
function PaginationContent({
className,
...props
}: React.ComponentProps<"ul">) {
return (
<ul
data-slot="pagination-content"
className={cn(
// Previous and Next sit at the edges, the page numbers in between.
"flex w-full min-w-0 items-center justify-center gap-0.5 [&>li:has([data-edge=next])]:ml-auto [&>li:has([data-edge=previous])]:mr-auto",
className
)}
{...props}
/>
);
}
function PaginationItem({ ...props }: React.ComponentProps<"li">) {
return <li data-slot="pagination-item" {...props} />;
}
type PaginationLinkProps = {
isActive?: boolean;
} & Pick<React.ComponentProps<typeof Button>, "size" | "variant"> &
React.ComponentProps<"a">;
function PaginationLink({
className,
children,
isActive,
size = "icon",
variant,
...props
}: PaginationLinkProps) {
// Page cells have no transition, so the old number does not fade on change.
// A link with its own variant (Previous, Next) keeps the button feel.
const cell = variant === undefined;
return (
<Button
variant={variant ?? (isActive ? "outline" : "ghost")}
size={size}
className={cn(
"rounded-[8px] text-sm font-medium",
cell && "[transition:none] active:not-aria-[haspopup]:scale-100",
cell && size === "icon" && "size-8",
cell &&
(isActive
? "border-border bg-popover text-foreground shadow-[shadow:var(--shadow-control,0_1px_2px_0_rgb(0_0_0/0.05))] hover:bg-popover"
: "text-[color:var(--foreground-secondary,var(--muted-foreground))] hover:bg-[var(--surface-strong,var(--muted))] hover:text-foreground dark:hover:bg-[var(--surface-strong,var(--muted))]"),
className
)}
nativeButton={false}
render={
<a
aria-current={isActive ? "page" : undefined}
data-slot="pagination-link"
data-active={isActive}
{...props}
>
{children}
</a>
}
/>
);
}
function PaginationPrevious({
className,
text = "Previous",
...props
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
return (
<PaginationLink
aria-label="Go to previous page"
data-edge="previous"
size="sm"
variant="outline"
className={cn("rounded-[8px]", className)}
{...props}
>
<RiArrowLeftLine data-icon="inline-start" />
<span className="hidden sm:block">{text}</span>
</PaginationLink>
);
}
function PaginationNext({
className,
text = "Next",
...props
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
return (
<PaginationLink
aria-label="Go to next page"
data-edge="next"
size="sm"
variant="outline"
className={cn("rounded-[8px]", className)}
{...props}
>
<span className="hidden sm:block">{text}</span>
<RiArrowRightLine data-icon="inline-end" />
</PaginationLink>
);
}
function PaginationEllipsis({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
aria-hidden
data-slot="pagination-ellipsis"
className={cn(
"flex size-8 items-center justify-center rounded-[8px] text-sm font-medium text-[color:var(--foreground-subtle,var(--muted-foreground))]",
className
)}
{...props}
>
<span aria-hidden>…</span>
<span className="sr-only">More pages</span>
</span>
);
}
export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
PAGINATION_DOTS,
paginationRange,
}; On this page