MRT logoMaterial React Table

On This Page

    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 Table Options

    1
    boolean
    true
    2
    () => MRT_RowModel<TData>
    3
    boolean
    TanStack Table Pagination Docs
    4
    Partial<PaginationProps> | ({ table }) => Partial<PaginationProps>
    Material UI TablePagination Props
    5
    OnChangeFn<PaginationState>
    TanStack Table Pagination Docs
    6
    number
    TanStack Table Pagination Docs
    7
    boolean
    TanStack Table Expanding Docs
    8
    'bottom' | 'top' | 'both'
    'bottom'
    9
    number

    Relevant State Options

    1
    { pageIndex: number, pageSize: number }
    { pageIndex: 0, pageSize: 10 }
    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

    Pagination State

    If you want to change the rows per page value or the initial page index, you can do that through either the initialState or state table option.

    //using initialState if you do not need to manage the pagination state yourself
    const table = useMaterialReactTable({
    columns,
    data,
    initialState: { pagination: { pageSize: 25, pageIndex: 2 } }, //customize the default page size and page index
    });
    //using state if you want to manage the pagination state yourself
    const [pagination, setPagination] = useState({
    pageIndex: 0,
    pageSize: 5, //customize the default page size
    });
    const table = useMaterialReactTable({
    columns,
    data,
    onPaginationChange: setPagination, //hoist pagination state to your state when it changes internally
    state: { pagination }, //pass the pagination state to the table
    });

    Note: Do not pass pagination state to both initialState and state at the same time. The state will overwrite the initialState values.

    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 of TablePaginationProps.

    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',
    });

    Demo

    Open StackblitzOpen Code SandboxOpen on GitHub
    MasonAndersonmanderson57@yopmail.comSeattle
    NoraBishopnbishop26@mailinator.comPortland
    LiamPattersonlpatterson61@yopmail.comAustin
    HarperRosshross38@mailinator.comChicago
    OliverBakerobaker72@yopmail.comMiami
    CharlottePhillipscphillips33@mailinator.comLos Angeles
    HenryCooperhcooper18@yopmail.comDenver
    EmmaJenkinsejenkins49@mailinator.comBoston
    AlexanderGonzalezagonzalez67@yopmail.comDallas
    AvaRamirezaramirez94@mailinator.comHouston

    Source Code

    1import {
    2 MaterialReactTable,
    3 useMaterialReactTable,
    4} from 'material-react-table';
    5import { columns, data } from './makeData';
    6
    7const 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 });
    19
    20 return <MaterialReactTable table={table} />;
    21};
    22
    23export 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 state
    const [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 internally
    state: { 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.

    Demo

    No records to display

    0-0 of 0

    Source Code

    1import { useEffect, useMemo, useState } from 'react';
    2import {
    3 MaterialReactTable,
    4 useMaterialReactTable,
    5 type MRT_ColumnDef,
    6 type MRT_ColumnFiltersState,
    7 type MRT_PaginationState,
    8 type MRT_SortingState,
    9} from 'material-react-table';
    10
    11type UserApiResponse = {
    12 data: Array<User>;
    13 meta: {
    14 totalRowCount: number;
    15 };
    16};
    17
    18type User = {
    19 firstName: string;
    20 lastName: string;
    21 address: string;
    22 state: string;
    23 phoneNumber: string;
    24};
    25
    26const Example = () => {
    27 //data and fetching state
    28 const [data, setData] = useState<User[]>([]);
    29 const [isError, setIsError] = useState(false);
    30 const [isLoading, setIsLoading] = useState(false);
    31 const [isRefetching, setIsRefetching] = useState(false);
    32 const [rowCount, setRowCount] = useState(0);
    33
    34 //table state
    35 const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(
    36 [],
    37 );
    38 const [globalFilter, setGlobalFilter] = useState('');
    39 const [sorting, setSorting] = useState<MRT_SortingState>([]);
    40 const [pagination, setPagination] = useState<MRT_PaginationState>({
    41 pageIndex: 0,
    42 pageSize: 10,
    43 });
    44
    45 //if you want to avoid useEffect, look at the React Query example instead
    46 useEffect(() => {
    47 const fetchData = async () => {
    48 if (!data.length) {
    49 setIsLoading(true);
    50 } else {
    51 setIsRefetching(true);
    52 }
    53
    54 const url = new URL(
    55 '/api/data',
    56 process.env.NODE_ENV === 'production'
    57 ? 'https://www.material-react-table.com'
    58 : 'http://localhost:3000',
    59 );
    60 url.searchParams.set(
    61 'start',
    62 `${pagination.pageIndex * pagination.pageSize}`,
    63 );
    64 url.searchParams.set('size', `${pagination.pageSize}`);
    65 url.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
    66 url.searchParams.set('globalFilter', globalFilter ?? '');
    67 url.searchParams.set('sorting', JSON.stringify(sorting ?? []));
    68
    69 try {
    70 const response = await fetch(url.href);
    71 const json = (await response.json()) as UserApiResponse;
    72 setData(json.data);
    73 setRowCount(json.meta.totalRowCount);
    74 } catch (error) {
    75 setIsError(true);
    76 console.error(error);
    77 return;
    78 }
    79 setIsError(false);
    80 setIsLoading(false);
    81 setIsRefetching(false);
    82 };
    83 fetchData();
    84 // eslint-disable-next-line react-hooks/exhaustive-deps
    85 }, [
    86 columnFilters, //re-fetch when column filters change
    87 globalFilter, //re-fetch when global filter changes
    88 pagination.pageIndex, //re-fetch when page index changes
    89 pagination.pageSize, //re-fetch when page size changes
    90 sorting, //re-fetch when sorting changes
    91 ]);
    92
    93 const columns = useMemo<MRT_ColumnDef<User>[]>(
    94 () => [
    95 {
    96 accessorKey: 'firstName',
    97 header: 'First Name',
    98 },
    99 //column definitions...
    117 ],
    118 [],
    119 );
    120
    121 const table = useMaterialReactTable({
    122 columns,
    123 data,
    124 enableRowSelection: true,
    125 getRowId: (row) => row.phoneNumber,
    126 initialState: { showColumnFilters: true },
    127 manualFiltering: true,
    128 manualPagination: true,
    129 manualSorting: true,
    130 muiToolbarAlertBannerProps: isError
    131 ? {
    132 color: 'error',
    133 children: 'Error loading data',
    134 }
    135 : undefined,
    136 onColumnFiltersChange: setColumnFilters,
    137 onGlobalFilterChange: setGlobalFilter,
    138 onPaginationChange: setPagination,
    139 onSortingChange: setSorting,
    140 rowCount,
    141 state: {
    142 columnFilters,
    143 globalFilter,
    144 isLoading,
    145 pagination,
    146 showAlertBanner: isError,
    147 showProgressBars: isRefetching,
    148 sorting,
    149 },
    150 });
    151
    152 return <MaterialReactTable table={table} />;
    153};
    154
    155export default Example;
    156

    View Extra Storybook Examples