Pagination
Rows are paged, ten to a page, with a pager under the table. Change the numbers, start on a page, or turn paging off and let virtualisation handle the scroll.
# | |||||
|---|---|---|---|---|---|
| 51 | Sofia Nair | Churned | Free | $0.00 | Sep 9, 2026 |
| 52 | Sofia Nguyen | Churned | Team | $0.00 | May 13, 2026 |
| 53 | Yuki Dubois | Active | Team | $174.00 | Jun 7, 2024 |
| 54 | Sofia Dubois | Active | Team | $1,363.00 | Sep 17, 2023 |
| 55 | Omar Bose | Active | Pro | $36.00 | Jun 13, 2023 |
| 56 | Sofia Dubois | Paused | Free | $0.00 | Dec 21, 2023 |
| 57 | Theo Kim | Paused | Pro | $48.00 | Nov 11, 2023 |
| 58 | Nia Tanaka | Active | Free | $0.00 | Jul 17, 2026 |
| 59 | Saanvi Prabhakaran | Active | Enterprise | $632.00 | Jun 17, 2023 |
| 60 | Arjun Khan | Active | Team | $1,624.00 | Oct 16, 2025 |
| 61 | Arjun Okafor | Active | Pro | $48.00 | Oct 29, 2026 |
| 62 | Liam Tanaka | Active | Pro | $24.00 | Jan 5, 2024 |
| 63 | Amelia Kim | Invited | Free | $0.00 | Oct 3, 2023 |
| 64 | Amelia Sharma | Active | Pro | $36.00 | Jul 1, 2025 |
| 65 | Olivia Menon | Paused | Enterprise | $1,501.00 | Jun 16, 2023 |
| 66 | Rohan Patel | Paused | Team | $1,189.00 | Oct 7, 2023 |
| 67 | Sofia Prabhakaran | Invited | Pro | $48.00 | Apr 6, 2023 |
| 68 | Arjun Nguyen | Active | Pro | $48.00 | Oct 13, 2024 |
| 69 | Mateo Patel | Active | Pro | $36.00 | Nov 6, 2024 |
| 70 | Meera Kim | Paused | Pro | $36.00 | Sep 12, 2024 |
| 71 | Kavya Patel | Active | Pro | $12.00 | Aug 28, 2023 |
| 72 | Yuki Reddy | Active | Free | $0.00 | Aug 2, 2025 |
| 73 | Zara Kim | Paused | Pro | $36.00 | Aug 9, 2025 |
| 74 | Vihaan Patel | Active | Free | $0.00 | Oct 12, 2024 |
| 75 | Saanvi Khan | Paused | Free | $0.00 | Jun 12, 2026 |
300 rows
Page 3 of 12
import { DataGrid, createDataGridColumns } from "@/components/ui/data-grid";
import { makePeople, PLAN_OPTIONS, STATUS_OPTIONS, type Person,} from "@/lib/data-grid-demo-data";
const col = createDataGridColumns<Person>();
const columns = col.columns([ col.rowNumber(), col.text("name", { header: "Name", size: 180 }), col.enum("status", { header: "Status", options: STATUS_OPTIONS, size: 120 }), col.enum("plan", { header: "Plan", options: PLAN_OPTIONS, size: 110 }), col.currency("mrr", { header: "MRR", currency: "USD", size: 120 }), col.date("joinedAt", { header: "Joined", size: 130 }),]);
const data = makePeople(300, 15);
/** 300 rows, 25 to a page, opened on page 3. The pager offers your sizes, and a go-to box past five pages. */export default function DataGridPaginationDemo() { return ( <DataGrid data={data} columns={columns} getRowId={(row) => row.id} selection={false} height={420} pagination={{ pageSize: 25, pageSizeOptions: [25, 50, 100, 300] }} initialState={{ pagination: { pageIndex: 2, pageSize: 25 } }} /> );}Use it
Section titled “Use it”<DataGrid data={data} columns={columns} /> // 10 a page; 10, 20, 50, 100 to choose from<DataGrid data={data} columns={columns} pagination={{ pageSize: 25 }} /> // 25 a page<DataGrid data={data} columns={columns} pagination={{ pageSize: 25, pageSizeOptions: [25, 50, 100] }} /><DataGrid data={data} columns={columns} pagination={false} /> // every row, no pagerThe page size comes from pagination.pageSize, else initialState.pagination.pageSize, else 10.
The size in use is always offered in the “rows per page” select, even when it is not one of the
options, so a persisted or initial size never shows as blank.
Start on a page
Section titled “Start on a page”<DataGrid data={data} columns={columns} initialState={{ pagination: { pageIndex: 2, pageSize: 25 } }} />pageIndex is zero-based: 2 is page 3. With persist on, the saved page size wins over
initialState; the page index is never persisted.
The pager
Section titled “The pager”DataGridPagination renders, left to right:
| Part | What it shows |
|---|---|
| The count | “x of y row(s) selected.” when selection is on, otherwise “y rows”. y is the row count after filtering. |
| Rows per page | A select over pageSizeOptions. The label hides on narrow screens; the select stays. |
| Page x of y | The position. |
| Go to page | A number box, shown once there are more than five pages. Type a page and press Enter. |
| First, previous, next, last | Buttons, disabled at the ends. First and last hide below the lg breakpoint. |
<DataGrid pager={false} /> // no pager; rows still page<DataGrid pager={{ showGoTo: true, showSelection: false }} /> // always show the go-to box, never the selected countWhen you compose the parts yourself, render <DataGridPagination /> wherever you like, above or
below the table. It renders nothing when pagination is false.
How it behaves
Section titled “How it behaves”- Paging happens last: after filtering, sorting and grouping, so a page is a slice of the result you see.
- The page index resets to 0 whenever the result changes: new
data, a filter, the search, a sort or a grouping change. That is TanStack’sautoResetPageIndex, on for client-side grids and off for server-side ones. Settable={{ autoResetPageIndex: false }}to keep the page through data updates (polling, say), and reset it yourself withgrid.table.setPageIndex(0)when a filter changes. - Expanded sub-rows do not count toward the page (
paginateExpandedRows: false): a page holdspageSizetop-level rows, and their children render beneath them on the same page, so an expanded page shows more thanpageSizerows. The same goes for group rows. - With paging on, only the current page is in the row model, so
virtualisation rarely kicks in; with
pagination: falsethe whole result is one scrolling list and virtualisation takes over past the threshold. - The page size is part of the persisted view and of saved views; see persistence.
- Selection is per row id, so it survives paging: select on page 1, page on, and the count in the pager still includes them.
- The state is
grid.table.state.pagination({ pageIndex, pageSize }), withsetPageIndex,setPageSize,nextPage,previousPage,firstPage,lastPage,getPageCount()andgetRowCount()on the table.
Options
Section titled “Options”| Option | Type | Default | What it does |
|---|---|---|---|
pagination |
false or object |
10 a page; 10, 20, 50, 100 | { pageSize?, pageSizeOptions? } sets the size and the choices; false shows every row with no pager. |
initialState.pagination |
object | page 1 | { pageIndex?, pageSize? }: the starting page and, failing pagination.pageSize, the size. |
pager |
false or object |
true |
On <DataGrid>: hide the pager, or configure it with { showSelection?, showGoTo? }. |
server.rowCount |
number | The total from the server; server.pageCount when a page is not rowCount / pageSize. |
|
state.pageIndex |
adapter | With state.onPageIndexChange, another owner for the page index (the URL). |
|
table.autoResetPageIndex |
boolean | true client-side, false server-side |
Back to page 1 when the result changes. |
| Labels | strings | English | rowsPerPage, pageOf, goToPage, selectedOf and rowsCount in labels; see internationalisation. |
See also
Section titled “See also”- Virtualization: the other way to handle many rows.
- Server-side data: when the server pages.
- URL state: the page in the address bar.