Pagination
BetaDivide una lista larga en páginas. Cuatro patrones según cuánto contexto necesita la persona.
Abrir en Figma
registry/ds/pagination.tsx"use client";
import { useState } from "react";
import { Pagination } from "@/registry/ds/pagination";
export default function PaginationBasic() {
const [page, setPage] = useState(6);
return <Pagination page={page} count={20} onPageChange={setPage} />;
}Qué patrón
| Patrón | Cuándo |
|---|---|
numbered | Listas y tablas donde saltar a una página tiene sentido y el total importa. |
prev-next | Pie de tabla con resumen ("Mostrando 1 a 10 de 97"). El más claro. |
compact | Poco espacio: mobile, paneles laterales, cards. |
arrows | Carruseles y galerías, donde el número de página no aporta. |
"use client";
import { useState } from "react";
import { Pagination } from "@/registry/ds/pagination";
export default function PaginationPatterns() {
const [page, setPage] = useState(1);
const from = (page - 1) * 10 + 1;
return (
<div className="flex w-full max-w-xl flex-col gap-8">
<Pagination label="Paginación con resumen" variant="prev-next" page={page} count={10} onPageChange={setPage} summary={`Mostrando ${from} a ${Math.min(from + 9, 97)} de 97 resultados`} />
<div className="flex flex-wrap items-center justify-between gap-6">
<Pagination label="Paginación compacta" variant="compact" size="sm" page={page} count={10} onPageChange={setPage} />
<Pagination label="Paginación con flechas" variant="arrows" shape="circle" page={page} count={10} onPageChange={setPage} />
</div>
</div>
);
}Forma y tamaño
"use client";
import { useState } from "react";
import { Pagination } from "@/registry/ds/pagination";
export default function PaginationShapes() {
const [page, setPage] = useState(2);
return (
<div className="flex flex-col items-center gap-5">
<Pagination label="Paginación chica" size="sm" page={page} count={5} onPageChange={setPage} />
<Pagination label="Paginación circular" shape="circle" page={page} count={5} onPageChange={setPage} />
</div>
);
}Reglas
- Es neutro a propósito. La página actual se marca con fondo gris y peso Medium, no con el verde de marca: la paginación no compite con los CTAs.
- Siempre visibles la primera y la última página; en el medio, elipsis.
- Anterior en la página 1 y Siguiente en la última quedan deshabilitados, no desaparecen: que el control no salte de lugar.
- Al cambiar de página, llevá el scroll al inicio de la lista.
- Menos de ~25 ítems: no pagines. Feeds y descubrimiento: "Cargar más".
Medidas
| Size | Celda | Texto | Icono | Radio Square |
|---|---|---|---|---|
sm | 32 | Text sm | 16 | radius-sm 6 |
md | 40 | Text sm | 20 | radius-md 8 |
Gap entre celdas spacing-xs (4). Circle: radius-full. Botones Anterior /
Siguiente: alto de la celda, padding X 12 / 14.
Tokens por estado
| Parte | Propiedad | Token |
|---|---|---|
| Número | Color | text/muted |
| Número · hover | Fondo | bg/subtle |
| Página actual | Fondo | bg/surface |
| Página actual | Borde | border/default |
| Página actual | Texto · Medium | text/default |
| Deshabilitado | Color | text/disabled |
| Elipsis | Color | text/subtle |
| Botón neutro | Fondo | bg/elevated |
| Botón neutro | Borde → hover | border/default |
| Botón neutro | Sombra | Elevation/xs |
| Foco | Anillo 2 px | focus/ring |
En Figma
Cinco piezas: .Page item y un componente por patrón.
| Propiedad en Figma | Valores | En código | Nota |
|---|---|---|---|
| Pagination / — Prev/Next / — Compact / — Arrows | Cuatro componentes | variant | — |
| Size | sm · md | size | — |
| Shape | Square · Circle | shape | — |
| Prev/Next › Content | Numbers · Results | summary | Pasar summary muestra el texto; sin él, los números. |
| .Page item › State | Default · Hover · Current · Disabled | page | Se calcula solo. |
Instalación
npx shadcn@latest add https://polen.cdc.cool/r/pagination.jsonPlayground
Configurá el componente y copiá el snippet: se genera del mismo estado que renderiza el preview.
variant
size
Forma
Páginas
Vecinas
const [page, setPage] = useState(1);
<Pagination page={page} count={10} onPageChange={setPage} />Uso
Es un componente controlado: vos guardás la página.
const [page, setPage] = useState(1);
<Pagination page={page} count={totalPages} onPageChange={setPage} />Props
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
page | number | — | Página actual, desde 1. Requerido. |
count | number | — | Total de páginas. Requerido. |
onPageChange | (page: number) => void | — | Requerido. |
variant | "numbered" | "prev-next" | "compact" | "arrows" | "numbered" | El patrón. |
size | "sm" | "md" | "md" | Celdas de 32 / 40. |
shape | "square" | "circle" | "square" | Forma de las celdas y botones. |
siblings | number | 1 | Páginas visibles a cada lado de la actual. |
summary | ReactNode | — | Texto central del patrón prev-next. |
label | string | "Paginación" | Nombre accesible del nav. |
También se exporta pageRange(page, count, siblings) por si necesitás armar
una paginación propia con la misma lógica de elipsis.
<nav aria-label="Paginación">: aparece en la lista de regiones del lector de pantalla.- Cada número se anuncia como "Página 6"; la actual lleva
aria-current="page". - Las flechas tienen nombre ("Página anterior" / "Página siguiente") y se deshabilitan de verdad en los extremos.
- En
compact, el cambio "6 / 20" se anuncia conaria-live. - La celda
smmide 32: cumple el mínimo de 24, pero en mobile preferímd. - Después de cambiar de página, mové el foco al inicio de los resultados.