Pagination Feature Guide
Client-side pagination is enabled by default in Material React Table. There are a number of ways to customize pagination, turn off pagination, or completely replace the built-in pagination with your own manual or server-side pagination logic.
Relevant Props
# | Prop Name | Type | Default Value | More Info Links | |
---|---|---|---|---|---|
1 |
|
| |||
2 |
| ||||
3 |
| TanStack Table Pagination Docs | |||
4 |
| Material UI TablePagination Props | |||
5 |
| TanStack Table Pagination Docs | |||
6 |
| TanStack Table Pagination Docs | |||
7 |
| TanStack Table Expanding Docs | |||
8 |
| ||||
9 |
| ||||
Relevant State Options
# | State Option | Type | Default Value | More Info Links | |
---|---|---|---|---|---|
1 |
|
| TanStack Table Pagination Docs | ||
Disable Pagination
If you simply want to disable pagination, you can set the enablePagination
table option to false
. This will both hide the pagination controls and disable the pagination functionality.
If you only want to disable the pagination logic, but still want to show and use the pagination controls, take a look down below at the Manual Pagination docs.
const table = useMaterialReactTable({columns,data,enablePagination: false,enableBottomToolbar: false, //hide the bottom toolbar as well if you want});
Customize Pagination
Customize Pagination Behavior
There are a few table options that you can use to customize the pagination behavior. The first one is autoResetPageIndex
. This table option is true
by default, and causes a table to automatically reset the table back to the first page whenever sorting, filtering, or grouping occurs. This makes sense for most use cases, but if you want to disable this behavior, you can set this table option to false
.
Next there is paginateExpandedRows
, which works in conjunction expanding features. This table option is true
by default, and forces the table to still only render the same number of rows per page that is set as the page size, even as sub-rows become expanded. However, this does cause expanded rows to sometimes not be on the same page as their parent row, so you can turn this off to keep sub rows with their parent row on the same page.
Customize Pagination Components
Note: In v2, muiPaginationProps is now based on mui's
PaginationProps
instead ofTablePaginationProps
.
You can customize the pagination component with the muiPaginationProps
table option to change things like the rowsPerPageOptions
or whether or not to show the first and last page buttons, and more.
const table = useMaterialReactTable({columns,data,muiPaginationProps: {rowsPerPageOptions: ['5', '10', '20'],showFirstButton: false,showLastButton: false,},});
Alternate Pagination UI
New in v2
By default, Material React Table provides its own Table Pagination UI that is more compact and traditional for data tables. However, if you want to use the Material Pagination component instead, it is as easy as setting the paginationDisplayMode
table option to pages
.
const table = useMaterialReactTable({columns,data,paginationDisplayMode: 'pages',});
First Name | Last Name | Email | City |
---|---|---|---|
Mason | Anderson | manderson57@yopmail.com | Seattle |
Nora | Bishop | nbishop26@mailinator.com | Portland |
Liam | Patterson | lpatterson61@yopmail.com | Austin |
Harper | Ross | hross38@mailinator.com | Chicago |
Oliver | Baker | obaker72@yopmail.com | Miami |
Charlotte | Phillips | cphillips33@mailinator.com | Los Angeles |
Henry | Cooper | hcooper18@yopmail.com | Denver |
Emma | Jenkins | ejenkins49@mailinator.com | Boston |
Alexander | Gonzalez | agonzalez67@yopmail.com | Dallas |
Ava | Ramirez | aramirez94@mailinator.com | Houston |
1import {2 MaterialReactTable,3 useMaterialReactTable,4} from 'material-react-table';5import { columns, data } from './makeData';67const Example = () => {8 const table = useMaterialReactTable({9 columns,10 data,11 muiPaginationProps: {12 color: 'primary',13 shape: 'rounded',14 showRowsPerPage: false,15 variant: 'outlined',16 },17 paginationDisplayMode: 'pages',18 });1920 return <MaterialReactTable table={table} />;21};2223export default Example;24
Manual or Server-Side Pagination
Manual Pagination
The default pagination features are client-side. This means you have to have all of your data fetched and stored in the table all at once. This may not be ideal for large datasets, but do not worry, Material React Table supports server-side pagination.
When the manualPagination
table option is set to true
, Material React Table will assume that the data
that is passed to the table already has had the pagination logic applied. Usually you would do this in your back-end logic.
Override Page Count and Row Count
If you are using manual pagination, the default page count and row count in the MRT Pagination component will be incorrect, as it is only derived from the number of rows provided in the client-side data
table option. Luckily, you can override these values and set your own page count or row count in the pageCount
and rowCount
table options.
const table = useMaterialReactTable({columns,data,manualPagination: true,rowCount: data.meta.totalDBRowCount, //you can tell the pagination how many rows there are in your back-end data});
Manage Pagination State
For either client-side or server-side pagination, you may want to have access to the pagination state yourself. You can do this like so with state
:
//store pagination state in your own stateconst [pagination, setPagination] = useState({pageIndex: 0,pageSize: 5, //customize the default page size});useEffect(() => {//do something when the pagination state changes}, [pagination.pageIndex, pagination.pageSize]);const table = useMaterialReactTable({columns,data,onPaginationChange: setPagination, //hoist pagination state to your state when it changes internallystate: { pagination }, //pass the pagination state to the table});return <MaterialReactTable table={table} />;
Alternatively, if all you care about is customizing the initial pagination state and do not need to react to its changes, like customizing the default page size or the page index, you can do that like so with initialState
:
const table = useMaterialReactTable({columns,data,initialState: { pagination: { pageSize: 25, pageIndex: 2 } },});
Here is the full Remote Data example showing off server-side filtering, pagination, and sorting.
First Name | Last Name | Address | State | Phone Number | |
---|---|---|---|---|---|
No records to display |
0-0 of 0
1import { useEffect, useMemo, useState } from 'react';2import {3 MaterialReactTable,4 type MRT_ColumnDef,5 type MRT_ColumnFiltersState,6 type MRT_PaginationState,7 type MRT_SortingState,8} from 'material-react-table';910type UserApiResponse = {11 data: Array<User>;12 meta: {13 totalRowCount: number;14 };15};1617type User = {18 firstName: string;19 lastName: string;20 address: string;21 state: string;22 phoneNumber: string;23};2425const Example = () => {26 //data and fetching state27 const [data, setData] = useState<User[]>([]);28 const [isError, setIsError] = useState(false);29 const [isLoading, setIsLoading] = useState(false);30 const [isRefetching, setIsRefetching] = useState(false);31 const [rowCount, setRowCount] = useState(0);3233 //table state34 const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(35 [],36 );37 const [globalFilter, setGlobalFilter] = useState('');38 const [sorting, setSorting] = useState<MRT_SortingState>([]);39 const [pagination, setPagination] = useState<MRT_PaginationState>({40 pageIndex: 0,41 pageSize: 10,42 });4344 //if you want to avoid useEffect, look at the React Query example instead45 useEffect(() => {46 const fetchData = async () => {47 if (!data.length) {48 setIsLoading(true);49 } else {50 setIsRefetching(true);51 }5253 const url = new URL(54 '/api/data',55 process.env.NODE_ENV === 'production'56 ? 'https://www.material-react-table.com'57 : 'http://localhost:3000',58 );59 url.searchParams.set(60 'start',61 `${pagination.pageIndex * pagination.pageSize}`,62 );63 url.searchParams.set('size', `${pagination.pageSize}`);64 url.searchParams.set('filters', JSON.stringify(columnFilters ?? []));65 url.searchParams.set('globalFilter', globalFilter ?? '');66 url.searchParams.set('sorting', JSON.stringify(sorting ?? []));6768 try {69 const response = await fetch(url.href);70 const json = (await response.json()) as UserApiResponse;71 setData(json.data);72 setRowCount(json.meta.totalRowCount);73 } catch (error) {74 setIsError(true);75 console.error(error);76 return;77 }78 setIsError(false);79 setIsLoading(false);80 setIsRefetching(false);81 };82 fetchData();83 // eslint-disable-next-line react-hooks/exhaustive-deps84 }, [85 columnFilters,86 globalFilter,87 pagination.pageIndex,88 pagination.pageSize,89 sorting,90 ]);9192 const columns = useMemo<MRT_ColumnDef<User>[]>(93 () => [94 {95 accessorKey: 'firstName',96 header: 'First Name',97 },98 //column definitions...116 ],117 [],118 );119120 return (121 <MaterialReactTable122 columns={columns}123 data={data}124 enableRowSelection125 getRowId={(row) => row.phoneNumber}126 initialState={{ showColumnFilters: true }}127 manualFiltering128 manualPagination129 manualSorting130 muiToolbarAlertBannerProps={131 isError132 ? {133 color: 'error',134 children: 'Error loading data',135 }136 : undefined137 }138 onColumnFiltersChange={setColumnFilters}139 onGlobalFilterChange={setGlobalFilter}140 onPaginationChange={setPagination}141 onSortingChange={setSorting}142 rowCount={rowCount}143 state={{144 columnFilters,145 globalFilter,146 isLoading,147 pagination,148 showAlertBanner: isError,149 showProgressBars: isRefetching,150 sorting,151 }}152 />153 );154};155156export default Example;157
View Extra Storybook Examples