Pagination
Page navigation with numbered buttons, prev/next, and ellipsis.
Installation#
npx @aniui/cli add paginationWeb preview — components render natively on iOS & Android
import { Pagination } from "@/components/ui/pagination";
export function MyScreen() {
const [page, setPage] = useState(1);
return (
<Pagination
total={10}
current={page}
onPageChange={setPage}
siblings={1}
/>
);
}Usage#
app/index.tsx
import { Pagination } from "@/components/ui/pagination";
export function MyScreen() {
const [page, setPage] = useState(1);
return (
<Pagination
total={10}
current={page}
onPageChange={setPage}
siblings={1}
/>
);
}Props#
PropTypeDefault
totalnumber-currentnumber-onPageChange(page: number) => void-siblingsnumber1classNamestring-Also accepts all View props.
Accessibility#
- Page navigation with
accessibilityStateon buttons to indicate current page. - Previous/next buttons are disabled at boundaries and announced as such.
Source#
components/ui/pagination.tsx
import React from "react";
import { View, Pressable, Text } from "react-native";
import { cn } from "@/lib/utils";
export interface PaginationProps extends React.ComponentPropsWithoutRef<typeof View> {
className?: string;
total: number;
current: number;
onPageChange: (page: number) => void;
siblings?: number;
}
function getPages(total: number, current: number, siblings: number): (number | "...")[] {
const pages: (number | "...")[] = [];
const start = Math.max(1, current - siblings);
const end = Math.min(total, current + siblings);
if (start > 1) { pages.push(1); if (start > 2) pages.push("..."); }
for (let i = start; i <= end; i++) pages.push(i);
if (end < total) { if (end < total - 1) pages.push("..."); pages.push(total); }
return pages;
}
export function Pagination({
className,
total,
current,
onPageChange,
siblings = 1,
...props
}: PaginationProps) {
const pages = getPages(total, current, siblings);
return (
<View className={cn("flex-row items-center justify-center gap-1", className)} {...props}>
<Pressable
onPress={() => onPageChange(current - 1)}
disabled={current <= 1}
accessible={true}
accessibilityRole="button"
accessibilityLabel="Previous page"
className="min-h-10 min-w-10 items-center justify-center rounded-md"
>
<Text className={cn("text-sm", current <= 1 ? "text-muted" : "text-foreground")}>‹</Text>
</Pressable>
{pages.map((page, i) =>
page === "..." ? (
<Text key={`e${i}`} className="text-muted-foreground px-1">…</Text>
) : (
<Pressable
key={page}
onPress={() => onPageChange(page)}
accessible={true}
accessibilityRole="button"
accessibilityLabel={`Page ${page}`}
accessibilityState={{ selected: page === current }}
className={cn(
"min-h-10 min-w-10 items-center justify-center rounded-md",
page === current ? "bg-primary" : "bg-transparent"
)}
>
<Text className={cn("text-sm font-medium", page === current ? "text-primary-foreground" : "text-foreground")}>
{page}
</Text>
</Pressable>
)
)}
<Pressable
onPress={() => onPageChange(current + 1)}
disabled={current >= total}
accessible={true}
accessibilityRole="button"
accessibilityLabel="Next page"
className="min-h-10 min-w-10 items-center justify-center rounded-md"
>
<Text className={cn("text-sm", current >= total ? "text-muted" : "text-foreground")}>›</Text>
</Pressable>
</View>
);
}