Pagination
Migration guide for Pagination from HeroUI v2 to v3
Refer to the v3 Pagination documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
Structure Changes
In v2, Pagination was a single component that handled all rendering internally via props:
import { Pagination } from "@heroui/react";
export default function App() {
return (
<Pagination total={10} initialPage={1} showControls />
);
}In v3, Pagination uses a compound component pattern where you compose each part explicitly:
import { Pagination } from "@heroui/react";
export default function App() {
return (
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous>
<Pagination.PreviousIcon />
</Pagination.Previous>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link isActive>1</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link>2</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link>3</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
<Pagination.Item>
<Pagination.Link>10</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Next>
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>
);
}Key Changes
1. Component Structure
v2: Single Pagination component that auto-generates page items from total prop
v3: Compound components: Pagination, Pagination.Summary, Pagination.Content, Pagination.Item, Pagination.Link, Pagination.Previous, Pagination.PreviousIcon, Pagination.Next, Pagination.NextIcon, Pagination.Ellipsis
2. Page Generation
v2: Pages auto-generated from total, siblings, boundaries props
v3: You compose page items manually, giving full control over layout and behavior. Build your own pagination logic or use a pagination hook.
3. Prop Changes
| v2 Prop | v3 Equivalent | Notes |
|---|---|---|
total | - | Removed (compose items manually) |
page | - | Manage active state via isActive on Pagination.Link |
initialPage | - | Manage state externally |
onChange | - | Use onPress on individual Pagination.Link components |
siblings | - | Removed (compose items manually) |
boundaries | - | Removed (compose items manually) |
dotsJump | - | Removed (handle ellipsis click manually) |
loop | - | Removed (implement manually) |
showControls | - | Compose Pagination.Previous and Pagination.Next |
isCompact | - | Removed (style with Tailwind CSS) |
showShadow | - | Removed (use Tailwind shadow-* classes) |
size | Pagination size | Same (sm, md, lg) |
variant | - | Removed (use Tailwind CSS) |
color | - | Removed (use Tailwind CSS) |
radius | - | Removed (use Tailwind CSS) |
isDisabled | isDisabled on Pagination.Link, Pagination.Previous, Pagination.Next | Per-item instead of global |
disableCursorAnimation | - | Removed |
disableAnimation | - | Removed |
renderItem | - | Compose items directly |
getItemAriaLabel | - | Set aria-label on individual items |
classNames | - | Use className on individual compound components |
4. Hook Changes
v2: usePagination hook available for custom implementations
v3: No built-in hook — compose pagination items directly or build your own pagination logic
Migration Examples
Basic Pagination
import { Pagination } from "@heroui/react";
<Pagination total={10} initialPage={1} />import { useState } from "react";
import { Pagination } from "@heroui/react";
const [page, setPage] = useState(1);
const totalPages = 10;
<Pagination>
<Pagination.Content>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
<Pagination.Item key={p}>
<Pagination.Link isActive={page === p} onPress={() => setPage(p)}>
{p}
</Pagination.Link>
</Pagination.Item>
))}
</Pagination.Content>
</Pagination>With Previous / Next Controls
<Pagination total={10} initialPage={1} showControls />const [page, setPage] = useState(1);
const totalPages = 10;
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous
isDisabled={page === 1}
onPress={() => setPage((p) => Math.max(1, p - 1))}
>
<Pagination.PreviousIcon />
</Pagination.Previous>
</Pagination.Item>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
<Pagination.Item key={p}>
<Pagination.Link isActive={page === p} onPress={() => setPage(p)}>
{p}
</Pagination.Link>
</Pagination.Item>
))}
<Pagination.Item>
<Pagination.Next
isDisabled={page === totalPages}
onPress={() => setPage((p) => Math.min(totalPages, p + 1))}
>
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>With Ellipsis
<Pagination total={20} initialPage={1} siblings={1} boundaries={1} />const [page, setPage] = useState(1);
const totalPages = 20;
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous
isDisabled={page === 1}
onPress={() => setPage((p) => Math.max(1, p - 1))}
>
<Pagination.PreviousIcon />
</Pagination.Previous>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link isActive={page === 1} onPress={() => setPage(1)}>
1
</Pagination.Link>
</Pagination.Item>
{page > 3 && (
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
)}
{[page - 1, page, page + 1]
.filter((p) => p > 1 && p < totalPages)
.map((p) => (
<Pagination.Item key={p}>
<Pagination.Link isActive={page === p} onPress={() => setPage(p)}>
{p}
</Pagination.Link>
</Pagination.Item>
))}
{page < totalPages - 2 && (
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
)}
<Pagination.Item>
<Pagination.Link
isActive={page === totalPages}
onPress={() => setPage(totalPages)}
>
{totalPages}
</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Next
isDisabled={page === totalPages}
onPress={() => setPage((p) => Math.min(totalPages, p + 1))}
>
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>Simple Previous / Next Only
{/* v2 didn't have a built-in simple prev/next pattern */}const [page, setPage] = useState(1);
const totalPages = 10;
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous
isDisabled={page === 1}
onPress={() => setPage((p) => p - 1)}
>
<Pagination.PreviousIcon />
<span>Previous</span>
</Pagination.Previous>
</Pagination.Item>
<Pagination.Item>
<Pagination.Next
isDisabled={page === totalPages}
onPress={() => setPage((p) => p + 1)}
>
<span>Next</span>
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>With Summary
{/* v2 didn't have a built-in summary slot */}const [page, setPage] = useState(1);
const perPage = 10;
const total = 100;
<Pagination>
<Pagination.Summary>
Showing {(page - 1) * perPage + 1}-{Math.min(page * perPage, total)} of {total}
</Pagination.Summary>
<Pagination.Content>
{/* Pagination items */}
</Pagination.Content>
</Pagination>Custom Icons
{/* v2 required renderItem for custom icons */}import { Icon } from "@iconify/react";
<Pagination>
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous onPress={() => setPage((p) => p - 1)}>
<Pagination.PreviousIcon>
<Icon icon="gravity-ui:arrow-left" />
</Pagination.PreviousIcon>
</Pagination.Previous>
</Pagination.Item>
{/* Page links */}
<Pagination.Item>
<Pagination.Next onPress={() => setPage((p) => p + 1)}>
<Pagination.NextIcon>
<Icon icon="gravity-ui:arrow-right" />
</Pagination.NextIcon>
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>Styling Changes
v2: classNames Prop
<Pagination
classNames={{
base: "custom-base",
wrapper: "custom-wrapper",
prev: "custom-prev",
next: "custom-next",
item: "custom-item",
cursor: "custom-cursor",
ellipsis: "custom-ellipsis",
}}
/>v3: Direct className Props
<Pagination className="custom-base">
<Pagination.Content className="custom-wrapper">
<Pagination.Item className="custom-item">
<Pagination.Previous className="custom-prev">
<Pagination.PreviousIcon />
</Pagination.Previous>
</Pagination.Item>
<Pagination.Item>
<Pagination.Link isActive className="custom-cursor">1</Pagination.Link>
</Pagination.Item>
<Pagination.Item>
<Pagination.Ellipsis className="custom-ellipsis" />
</Pagination.Item>
<Pagination.Item>
<Pagination.Next className="custom-next">
<Pagination.NextIcon />
</Pagination.Next>
</Pagination.Item>
</Pagination.Content>
</Pagination>Component Anatomy
The v3 Pagination follows this structure:
Pagination (Root, nav element)
├── Pagination.Summary (optional, info text)
└── Pagination.Content (ul container)
└── Pagination.Item (li wrapper, repeated)
├── Pagination.Previous (with Pagination.PreviousIcon)
├── Pagination.Link (page number, isActive for current)
├── Pagination.Ellipsis
└── Pagination.Next (with Pagination.NextIcon)Summary
- Component Structure: Single auto-generating component → compound components with manual composition
- Page Generation:
total/siblings/boundariesprops → compose page items manually with your own pagination logic - Active Page:
page/initialPageprops →isActiveprop on individualPagination.Link - Navigation Controls:
showControlsprop → composePagination.PreviousandPagination.Nextdirectly - Ellipsis: Auto-generated → compose
Pagination.Ellipsismanually where needed - Events: Single
onChange→ individualonPresshandlers on eachPagination.Link - New Features:
Pagination.Summaryfor showing result counts, custom icons viaPreviousIcon/NextIconchildren - Hook Removed:
usePagination→ build your own pagination logic - Styling Props Removed:
variant,color,radius,isCompact,showShadow→ use Tailwind CSS - ClassNames Removed: Use
classNameon individual compound components