Pagination

Add pagination to render a built-in pagination bar below the table. All state is managed internally. No extra props needed for basic usage.

Built-in pagination

30 rows. Use the buttons above to change the initial rows-per-page, or use the built-in selector inside the table.

Rows per page:30 total rows
Name
Department
Salary
Status
Aria Chen
Engineering
$80,000
Active
Marcus Webb
Product
$83,700
Remote
Priya Kapoor
Design
$87,400
On Leave
Jordan Ellis
Analytics
$91,100
Contractor
Sam Rivera
Sales
$94,800
Active

Pagination position

By default the pagination bar renders below the table. Use paginationPosition to move it above the table ("top") or show it in both places ("both").

Pagination position

Toggle between top, bottom, and both to see the pagination bar reposition.

Position:
Name
Department
Salary
Status
Aria Chen
Engineering
$80,000
Active
Marcus Webb
Product
$83,700
Remote
Priya Kapoor
Design
$87,400
On Leave
Jordan Ellis
Analytics
$91,100
Contractor
Sam Rivera
Sales
$94,800
Active

Server-side pagination

When your dataset is too large to load at once, delegate pagination to the server. Set paginationServer and provide paginationTotalRows so the pagination bar knows how many pages exist. Respond to onChangePage andonChangeRowsPerPage to fetch each page.

Server-side pagination

100-row simulated dataset with 300 ms fake network latency. Change pages or the rows-per-page selector to trigger a new fetch.

Name
Department
Salary
Status

Callback contract

When using server-side mode, the three callbacks are mutually exclusive. Only one fires per user interaction, never two at once:

User actionCallback that firesWhat you should do
Click a sortable column headeronSort(column, direction, rows)Fetch page 1 with the new sort. Update your local sort state and reset your local page to 1.
Click a page number / prev / nextonChangePage(page, totalRows)Fetch the requested page with current sort params.
Change the rows-per-page selectoronChangeRowsPerPage(newPerPage, currentPage)Fetch the recalculated page at the new page size with current sort params.

Sorting always resets to page 1. When the user sorts while on page 3, the pagination bar jumps back to page 1 automatically and onChangePage isnot called. onSort is the sole callback, and it is your responsibility to fetch from page 1 and keep your own page state in sync:

function handleSort(col: TableColumn<Employee>, dir: SortOrder) {
  const field = col.sortField ?? String(col.id);
  setSortField(field);
  setSortDir(dir);
  setPage(1); // keep local state in sync with the UI reset
  fetchData({ page: 1, perPage, sortField: field, sortDir: dir });
}

Combined: server-side sorting and pagination

Wire all three handlers together. Because sorting resets to page 1 and suppressesonChangePage, you only need one fetch per user action.

Server-side sort + pagination

150-row simulated dataset. Sort any column, then page through the results — each action triggers exactly one fetch (300 ms latency).

sort: "name" asc · page: 1 · perPage: 10 · total: 0
Name
Department
Salary
Status

Full example: search, sort, and pagination

A realistic pattern with an external search input. Searching is an external state change. It doesn't go through any of DataTable's callbacks, so togglepaginationResetDefaultPage yourself to reset the bar to page 1.

Search + sort + pagination

200-row simulated dataset with 350 ms fake network latency. Search, sort, and page. Each action triggers exactly one fetch.

Name
Department
Salary
Status

With React Query

Let TanStack Queryown the fetch. Update state in the callbacks and the query key change triggers a refetch automatically. placeholderData: keepPreviousData prevents an empty flash between page transitions.

import { useState } from 'react';
import { useQuery, keepPreviousData } from '@tanstack/react-query';
import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component';

export default function App() {
  const [page, setPage] = useState(1);
  const [perPage, setPerPage] = useState(10);
  const [sortField, setSortField] = useState('name');
  const [sortDir, setSortDir] = useState<SortOrder>(SortOrder.ASC);

  const { data, isLoading } = useQuery({
    queryKey: ['employees', page, perPage, sortField, sortDir],
    queryFn: () =>
      fetch(`/api/employees?page=${page}&limit=${perPage}&sort=${sortField}&dir=${sortDir}`).then(r => r.json()),
    placeholderData: keepPreviousData,
  });

  return (
    <DataTable
      columns={columns}
      data={data?.rows ?? []}
      progressPending={isLoading}
      pagination
      paginationServer
      paginationTotalRows={data?.total ?? 0}
      onChangePage={setPage}
      onChangeRowsPerPage={pp => {
        setPerPage(pp);
        setPage(1);
      }}
      sortServer
      onSort={(col, dir) => {
        setSortField(col.sortField ?? String(col.id));
        setSortDir(dir);
        setPage(1);
      }}
    />
  );
}

With SWR

import { useState } from 'react';
import useSWR from 'swr';
import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component';

const fetcher = (url: string) => fetch(url).then(r => r.json());

export default function App() {
  const [page, setPage] = useState(1);
  const [perPage, setPerPage] = useState(10);
  const [sort, setSort] = useState({ field: 'name', dir: SortOrder.ASC });

  const url = `/api/employees?page=${page}&limit=${perPage}&sort=${sort.field}&dir=${sort.dir}`;
  const { data, isLoading } = useSWR(url, fetcher);

  return (
    <DataTable
      columns={columns}
      data={data?.rows ?? []}
      progressPending={isLoading}
      pagination
      paginationServer
      paginationTotalRows={data?.total ?? 0}
      onChangePage={setPage}
      onChangeRowsPerPage={pp => {
        setPerPage(pp);
        setPage(1);
      }}
      sortServer
      onSort={(col, dir) => {
        setSort({ field: col.sortField ?? String(col.id), dir });
        setPage(1);
      }}
    />
  );
}

Resetting to page 1

For external state changes (search, filter) that aren't triggered by a table callback, toggle paginationResetDefaultPage to reset the bar to page 1. Any change to this boolean (regardless of direction) triggers the reset.

const [resetPage, setResetPage] = useState(false);

function handleSearch(q: string) {
  setSearch(q);
  setPage(1);
  setResetPage(prev => !prev);
  fetchData({ page: 1, perPage, sortField, sortDir, q });
}

Persisting selection across pages

By default, navigating between pages clears selected rows. Use paginationServerOptions to retain selections across page changes and sorts:

<DataTable
  pagination
  paginationServer
  paginationServerOptions={{
    persistSelectedOnPageChange: true,
    persistSelectedOnSort: true,
  }}
/>;

Custom pagination component

Replace the built-in pagination bar entirely by passing a component topaginationComponent. It receives rowsPerPage,rowCount, currentPage, onChangePage, andonChangeRowsPerPage as props.

import { type PaginationComponentProps } from 'react-data-table-component';

function MyPagination({ rowsPerPage, rowCount, currentPage, onChangePage }: PaginationComponentProps) {
  const pages = Math.ceil(rowCount / rowsPerPage);
  return (
    <div>
      <button disabled={currentPage === 1} onClick={() => onChangePage(currentPage - 1, rowCount)}>
        ‹ Prev
      </button>
      <span>
        Page {currentPage} of {pages}
      </span>
      <button disabled={currentPage === pages} onClick={() => onChangePage(currentPage + 1, rowCount)}>
        Next ›
      </button>
    </div>
  );
}

<DataTable columns={columns} data={data} pagination paginationComponent={MyPagination} />;

Localization

All navigation aria-labels on the built-in pagination bar are controlled via thelocalization prop. Use a pre-built locale or supply your own — every key is optional and falls back to the English default. Visible text such asrowsPerPageText and rangeSeparatorText is still set viapaginationComponentOptions.

// Drop-in locale
import DataTable from 'react-data-table-component';
import { fr } from 'react-data-table-component/locales';

<DataTable columns={columns} data={data} pagination localization={fr} />;
// Partial override — spread a locale and replace only what you need
import { fr } from 'react-data-table-component/locales';
import type { Localization } from 'react-data-table-component';

const myLocale: Localization = {
  ...fr,
  pagination: {
    ...fr.pagination,
    nextPageAriaLabel: 'Suivant',
  },
};

<DataTable columns={columns} data={data} pagination localization={myLocale} />;
// Built from scratch — every key is optional
import type { Localization } from 'react-data-table-component';

const custom: Localization = {
  pagination: {
    navigationAriaLabel: 'Table navigation',
    firstPageAriaLabel: 'First',
    previousPageAriaLabel: 'Prev',
    nextPageAriaLabel: 'Next',
    lastPageAriaLabel: 'Last',
  },
};

<DataTable columns={columns} data={data} pagination localization={custom} />;

Prop reference

PropTypeDefaultDescription
paginationbooleanfalseEnable built-in pagination
paginationPerPagenumber10Rows shown per page.
paginationPosition'top' | 'bottom' | 'both''bottom'Where the pagination bar renders. 'both' shows it above and below the table simultaneously.
paginationRowsPerPageOptionsnumber[][10,15,20,25,30]Options in the rows-per-page dropdown.
paginationDefaultPagenumber1Initial active page.
paginationPagenumber-Controlled active page. When provided, the table navigates to this page whenever the value changes. Use together with onChangePage to keep them in sync.
paginationResetDefaultPagebooleanfalseToggle to reset to page 1 (e.g. after a filter change). Prefer paginationPage for new code.
paginationTotalRowsnumber-Total row count for server-side pagination.
paginationServerbooleanfalseDelegate page changes to onChangePage / onChangeRowsPerPage.
paginationServerOptionsPaginationServerOptions-Selection-persistence options for server-side mode.
paginationComponentOptionsPaginationOptions-Localisation and display options for the built-in paginator.
paginationComponentReact.ComponentType-Replace the built-in pagination UI entirely.
paginationIconsPaginationIcons-Override any or all pagination icons. Pass a partial object: { next: <MyIcon /> }.
onChangePage(page, totalRows) => void-Called when the active page changes.
onChangeRowsPerPage(rowsPerPage, page) => void-Called when rows-per-page selection changes.
localizationLocalization-Override every user-visible string and aria-label in the table. Pass a pre-built locale or build your own.

See it combined with other features in the Server-side sort, page & filter recipe and URL-synced table state.