MRT logoMantine React Table

On This Page

Pagination Feature Guide

Client-side pagination is enabled by default in Mantine 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

#
Prop Name
Type
Default Value
More Info Links
1booleanTanStack Table Pagination Docs
2booleantrue
3() => MRT_RowModel<TData>
4PaginationProps & { rowsPerPageOptions?: string[], showRowsPerPage?: boolean; }Mantine Pagination Docs
5booleanTanStack Table Pagination Docs
6OnChangeFn<PaginationState>TanStack Table Pagination Docs
7numberTanStack Table Pagination Docs
8booleanTanStack Table Expanding Docs
9'default' | 'pages' | 'custom''default'MRT Editing Docs
10'bottom' | 'top' | 'both'
11number

Relevant State Options

#
State Option
Type
Default Value
More Info Links
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 = useMantineReactTable({
  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

You can customize the pagination component with the mantinePaginationProps table option to change things like the rowsPerPageOptions or whether or not to show the first and last page buttons, and more.

const table = useMantineReactTable({
  columns,
  data,
  mantinePaginationProps: {
    rowsPerPageOptions: ['5', '10'],
    withEdges: false, //note: changed from `showFirstLastButtons` in v1.0
  },
});

Alternate Pagination UI

By default, Mantine React Table provides its own Table Pagination UI that is more compact and traditional for data tables. However, if you want to use the Mantine Pagination component instead, it is as easy as setting the paginationDisplayMode table option to pages.

const table = useMantineReactTable({
  columns,
  data,
  paginationDisplayMode: 'pages',
});
First Name
Last Name
Email
City
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
import '@mantine/core/styles.css';
import '@mantine/dates/styles.css'; //if using mantine date picker features
import 'mantine-react-table/styles.css'; //make sure MRT styles were imported in your app root (once)
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table';
import { columns, data } from './makeData';

const Example = () => {
  const table = useMantineReactTable({
    columns,
    data,
    mantinePaginationProps: {
      showRowsPerPage: false,
    },
    paginationDisplayMode: 'pages',
  });

  return <MantineReactTable table={table} />;
};

export default Example;

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, Mantine React Table supports server-side pagination.

When the manualPagination table option is set to true, Mantine 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 = useMantineReactTable({
  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 = useMantineReactTable({
  columns,
  data,
  onPaginationChange: setPagination, //hoist pagination state to your state when it changes internally
  state: { pagination }, //pass the pagination state to the table
});

return <MantineReactTable 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 = useMantineReactTable({
  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

Rows per page

0-0 of 0

import '@mantine/core/styles.css';
import '@mantine/dates/styles.css'; //if using mantine date picker features
import 'mantine-react-table/styles.css'; //make sure MRT styles were imported in your app root (once)
import { useEffect, useMemo, useState } from 'react';
import {
  MantineReactTable,
  useMantineReactTable,
  type MRT_ColumnDef,
  type MRT_ColumnFiltersState,
  type MRT_PaginationState,
  type MRT_SortingState,
} from 'mantine-react-table';

type UserApiResponse = {
  data: Array<User>;
  meta: {
    totalRowCount: number;
  };
};

type User = {
  firstName: string;
  lastName: string;
  address: string;
  state: string;
  phoneNumber: string;
};

const Example = () => {
  //data and fetching state
  const [data, setData] = useState<User[]>([]);
  const [isError, setIsError] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  const [isRefetching, setIsRefetching] = useState(false);
  const [rowCount, setRowCount] = useState(0);

  //table state
  const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(
    [],
  );
  const [globalFilter, setGlobalFilter] = useState('');
  const [sorting, setSorting] = useState<MRT_SortingState>([]);
  const [pagination, setPagination] = useState<MRT_PaginationState>({
    pageIndex: 0,
    pageSize: 10,
  });

  //if you want to avoid useEffect, look at the React Query example instead
  useEffect(() => {
    const fetchData = async () => {
      if (!data.length) {
        setIsLoading(true);
      } else {
        setIsRefetching(true);
      }

      const url = new URL(
        '/api/data',
        process.env.NODE_ENV === 'production'
          ? 'https://www.mantine-react-table.com'
          : 'http://localhost:3001',
      );
      url.searchParams.set(
        'start',
        `${pagination.pageIndex * pagination.pageSize}`,
      );
      url.searchParams.set('size', `${pagination.pageSize}`);
      url.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
      url.searchParams.set('globalFilter', globalFilter ?? '');
      url.searchParams.set('sorting', JSON.stringify(sorting ?? []));

      try {
        const response = await fetch(url.href);
        const json = (await response.json()) as UserApiResponse;
        setData(json.data);
        setRowCount(json.meta.totalRowCount);
      } catch (error) {
        setIsError(true);
        console.error(error);
        return;
      }
      setIsError(false);
      setIsLoading(false);
      setIsRefetching(false);
    };
    fetchData();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    columnFilters, //refetch when column filters change
    globalFilter, //refetch when global filter changes
    pagination.pageIndex, //refetch when page index changes
    pagination.pageSize, //refetch when page size changes
    sorting, //refetch when sorting changes
  ]);

  const columns = useMemo<MRT_ColumnDef<User>[]>(
    () => [
      {
        accessorKey: 'firstName',
        header: 'First Name',
      },

      {
        accessorKey: 'lastName',
        header: 'Last Name',
      },
      {
        accessorKey: 'address',
        header: 'Address',
      },
      {
        accessorKey: 'state',
        header: 'State',
      },
      {
        accessorKey: 'phoneNumber',
        header: 'Phone Number',
      },
    ],
    [],
  );

  const table = useMantineReactTable({
    columns,
    data,
    enableRowSelection: true,
    getRowId: (row) => row.phoneNumber,
    initialState: { showColumnFilters: true },
    manualFiltering: true,
    manualPagination: true,
    manualSorting: true,
    rowCount,
    onColumnFiltersChange: setColumnFilters,
    onGlobalFilterChange: setGlobalFilter,
    onPaginationChange: setPagination,
    onSortingChange: setSorting,
    state: {
      columnFilters,
      globalFilter,
      isLoading,
      pagination,
      showAlertBanner: isError,
      showProgressBars: isRefetching,
      sorting,
    },
    mantineToolbarAlertBannerProps: isError
      ? { color: 'red', children: 'Error loading data' }
      : undefined,
  });

  return <MantineReactTable table={table} />;
};

export default Example;

View Extra Storybook Examples