Components
- Accordion
- Alert
- Avatar
- Badge
- Breadcrumb
- Button
- Button Group
- Calendarnew
- Card
- Carousel
- Checkbox
- Chip
- Collapse
- Dialog
- Drawer
- Footer
- Gallery
- Icon Button
- Image
- Input
- List
- Mapnew
- Menu
- Navbar
- Pagination
- Popover
- Progress
- Radio
- Rating
- Select
- Sidebar
- Skeletonnew
- Slider
- Software Purchase Card
- Sonner / Toastnew
- Speed Dial
- Spinner
- Stepper
- Switch
- Table
- Tabs
- Textarea
- Timeline
- Tooltip
- Typography
- Video
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
const TOTAL_PAGES = 10
export function PaginationDemo() {
const [currentPage, setCurrentPage] = React.useState(2)
const pages = React.useMemo(() => {
const base = new Set<number>([1, TOTAL_PAGES])
for (let delta = -1; delta <= 1; delta += 1) {
const candidate = currentPage + delta
if (candidate > 1 && candidate < TOTAL_PAGES) {
base.add(candidate)
}
}
return Array.from(base).sort((a, b) => a - b)
}, [currentPage])
const goToPage = React.useCallback(
(page: number) => {
setCurrentPage(Math.min(Math.max(page, 1), TOTAL_PAGES))
},
[setCurrentPage]
)
const renderPageItems = () => {
const items: React.ReactNode[] = []
let lastPage: number | null = null
pages.forEach((page) => {
if (lastPage !== null && page - lastPage > 1) {
items.push(
<PaginationItem key={`ellipsis-${page}`}>
<PaginationEllipsis />
</PaginationItem>
)
}
items.push(
<PaginationItem key={page}>
<PaginationLink
href="#"
isActive={page === currentPage}
onClick={(event) => {
event.preventDefault()
goToPage(page)
}}
>
{page}
</PaginationLink>
</PaginationItem>
)
lastPage = page
})
return items
}
const isFirstPage = currentPage === 1
const isLastPage = currentPage === TOTAL_PAGES
return (
<Pagination className="justify-start md:justify-center">
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
className={cn(isFirstPage && "pointer-events-none opacity-40")}
aria-disabled={isFirstPage}
tabIndex={isFirstPage ? -1 : undefined}
onClick={(event) => {
event.preventDefault()
if (!isFirstPage) {
goToPage(currentPage - 1)
}
}}
/>
</PaginationItem>
{renderPageItems()}
<PaginationItem>
<PaginationNext
href="#"
className={cn(isLastPage && "pointer-events-none opacity-40")}
aria-disabled={isLastPage}
tabIndex={isLastPage ? -1 : undefined}
onClick={(event) => {
event.preventDefault()
if (!isLastPage) {
goToPage(currentPage + 1)
}
}}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
)
}
Installation
pnpm dlx @creative-tim/ui@latest add pagination
Usage
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
export default function Example() {
return (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="#" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#" isActive>
2
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href="#" />
</PaginationItem>
</PaginationContent>
</Pagination>
)
}Examples
Pagination Demo
Classic pagination with previous/next buttons, current page highlighting, and ellipsis for long page ranges.
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
const TOTAL_PAGES = 10
export function PaginationDemo() {
const [currentPage, setCurrentPage] = React.useState(2)
const pages = React.useMemo(() => {
const base = new Set<number>([1, TOTAL_PAGES])
for (let delta = -1; delta <= 1; delta += 1) {
const candidate = currentPage + delta
if (candidate > 1 && candidate < TOTAL_PAGES) {
base.add(candidate)
}
}
return Array.from(base).sort((a, b) => a - b)
}, [currentPage])
const goToPage = React.useCallback(
(page: number) => {
setCurrentPage(Math.min(Math.max(page, 1), TOTAL_PAGES))
},
[setCurrentPage]
)
const renderPageItems = () => {
const items: React.ReactNode[] = []
let lastPage: number | null = null
pages.forEach((page) => {
if (lastPage !== null && page - lastPage > 1) {
items.push(
<PaginationItem key={`ellipsis-${page}`}>
<PaginationEllipsis />
</PaginationItem>
)
}
items.push(
<PaginationItem key={page}>
<PaginationLink
href="#"
isActive={page === currentPage}
onClick={(event) => {
event.preventDefault()
goToPage(page)
}}
>
{page}
</PaginationLink>
</PaginationItem>
)
lastPage = page
})
return items
}
const isFirstPage = currentPage === 1
const isLastPage = currentPage === TOTAL_PAGES
return (
<Pagination className="justify-start md:justify-center">
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
className={cn(isFirstPage && "pointer-events-none opacity-40")}
aria-disabled={isFirstPage}
tabIndex={isFirstPage ? -1 : undefined}
onClick={(event) => {
event.preventDefault()
if (!isFirstPage) {
goToPage(currentPage - 1)
}
}}
/>
</PaginationItem>
{renderPageItems()}
<PaginationItem>
<PaginationNext
href="#"
className={cn(isLastPage && "pointer-events-none opacity-40")}
aria-disabled={isLastPage}
tabIndex={isLastPage ? -1 : undefined}
onClick={(event) => {
event.preventDefault()
if (!isLastPage) {
goToPage(currentPage + 1)
}
}}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
)
}
Pagination Circular
Rounded pill pagination that keeps controls compact while staying touch-friendly—great for dashboards and mobile layouts.
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
export function PaginationCircular() {
return (
<Pagination className="justify-start md:justify-center">
<PaginationContent className="gap-2">
<PaginationItem>
<PaginationPrevious
href="#"
className="bg-secondary/10 text-secondary-foreground hover:bg-secondary/20 rounded-full px-4"
/>
</PaginationItem>
{[1, 2, 3, 4, 5].map((page) => (
<PaginationItem key={page}>
<PaginationLink
href="#"
className="border-border rounded-full border px-4 py-2 text-sm font-medium"
isActive={page === 2}
>
{page}
</PaginationLink>
</PaginationItem>
))}
<PaginationItem>
<PaginationNext
href="#"
className="bg-secondary/10 text-secondary-foreground hover:bg-secondary/20 rounded-full px-4"
/>
</PaginationItem>
</PaginationContent>
</Pagination>
)
}
Pagination Group
Grouped pagination buttons with shared borders that feel like a segmented control for precise navigation.
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
export function PaginationGroup() {
return (
<Pagination className="justify-start md:justify-center">
<PaginationContent className="bg-background overflow-hidden rounded-lg border shadow-sm">
<PaginationItem>
<PaginationPrevious
href="#"
className="rounded-none border-r px-5 text-sm font-medium"
/>
</PaginationItem>
{[1, 2, 3, 4, 5].map((page) => (
<PaginationItem key={page}>
<PaginationLink
href="#"
className="rounded-none border-x px-5 text-sm font-semibold"
isActive={page === 3}
>
{page}
</PaginationLink>
</PaginationItem>
))}
<PaginationItem>
<PaginationNext
href="#"
className="rounded-none border-l px-5 text-sm font-medium"
/>
</PaginationItem>
</PaginationContent>
</Pagination>
)
}
Simple Pagination
Minimal pagination bar with icon buttons and a textual page indicator—perfect for tables or compact layouts.
Page 2 of 10
import { ChevronLeft, ChevronRight } from "lucide-react"
import { Button } from "@/components/ui/button"
export function SimplePagination() {
return (
<div className="flex items-center gap-3 rounded-full border px-4 py-2 shadow-sm">
<Button
variant="ghost"
size="icon"
className="border-border/60 text-muted-foreground hover:text-foreground rounded-full border"
>
<ChevronLeft className="h-4 w-4" />
</Button>
<p className="text-muted-foreground text-sm">
Page{" "}
<span className="text-foreground font-semibold" aria-live="polite">
2
</span>{" "}
of <span className="text-foreground font-semibold">10</span>
</p>
<Button
variant="ghost"
size="icon"
className="border-border/60 text-muted-foreground hover:text-foreground rounded-full border"
>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
)
}
Props
Pagination
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes for the <nav> tag. |
...props | React.ComponentProps<"nav"> | - | Native nav props such as role. |
PaginationContent
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes for the <ul> wrapper. |
...props | React.ComponentProps<"ul"> | - | Native list props. |
PaginationLink
| Prop | Type | Default | Description |
|---|---|---|---|
href | string | - | Destination for the link. |
isActive | boolean | false | Controls the active/outlined state. |
size | "default" | "icon" | "icon" | Matches the shadcn Button sizes. |
className | string | - | Additional classes for styling. |
...props | React.ComponentProps<"a"> | - | Anchor props like target or rel. |
PaginationPrevious / PaginationNext
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Extra classes for styling. |
...props | React.ComponentProps<typeof PaginationLink> | - | Accept the same props as PaginationLink. |
PaginationEllipsis
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Tailor spacing or color of the ellipsis dots. |
...props | React.ComponentProps<"span"> | - | Native span props. |
Best Practices
- Use ellipsis to keep long page lists concise and scannable.
- Pair pagination with results metadata (e.g., "241 results") so users understand context.
- Keep hit targets large enough for touch devices—circular or pill variants are great here.
- Disable previous/next links when users reach the ends of the collection.
- Provide keyboard focus styles so pagination stays accessible.
- Use descriptive
aria-labelattributes when pagination controls repeat across the page.
Pagination keeps big data sets manageable. Combine these controls with tables, cards, or galleries to give users confident navigation without overwhelming them.
Integrate the blocks in your application or use them in v0, Lovable, Claude, etc.
Speed up your workflow with modular, open-source components and blocksthat integrate effortlessly through Registries and MCPs.
Talk to an expert