Skip to content

TablePagination

Reusable pagination component for tables. Works with both client-side and server-side pagination.

Import

tsx
import { TablePagination } from '@/shared/ui/components/table/TablePagination'

Props

TablePaginationProps

PropTypeDefaultDescription
State
pageIndexnumberRequiredCurrent page index (0-based)
pageSizenumberRequiredCurrent page size
totalCountnumber-Total number of items (optional for cursor-based pagination)
Options
pageSizeOptionsnumber[][10, 20, 50, 100]Available page size options
Callbacks
onPageChange(pageIndex: number) => void-Called when page changes
onPageSizeChange(pageSize: number) => void-Called when page size changes
Navigation
canPreviousPagebooleanpageIndex > 0Disable previous button
canNextPagebooleantrueDisable next button
Labels (i18n)
labelsPaginationLabels-Custom labels for internationalization
Styling
classNamestring-Additional CSS classes

PaginationLabels

ts
interface PaginationLabels {
  rowsPerPage?: string  // Default: "Rows per page"
  page?: string         // Default: "Page"
  of?: string           // Default: "of"
}

Examples

Basic Usage

tsx
import { TablePagination } from '@/shared/ui/components/table/TablePagination'

function MyTable() {
  const [pageIndex, setPageIndex] = useState(0)
  const [pageSize, setPageSize] = useState(10)

  return (
    <>
      {/* Your table */}
      <table>...</table>

      {/* Pagination */}
      <TablePagination
        pageIndex={pageIndex}
        pageSize={pageSize}
        totalCount={250}
        onPageChange={setPageIndex}
        onPageSizeChange={(size) => {
          setPageSize(size)
          setPageIndex(0) // Reset to first page
        }}
      />
    </>
  )
}

With Custom Page Sizes

tsx
<TablePagination
  pageIndex={pageIndex}
  pageSize={pageSize}
  totalCount={1000}
  pageSizeOptions={[25, 50, 100, 250]}
  onPageChange={setPageIndex}
  onPageSizeChange={setPageSize}
/>

Internationalization

tsx
<TablePagination
  pageIndex={pageIndex}
  pageSize={pageSize}
  totalCount={totalCount}
  onPageChange={setPageIndex}
  onPageSizeChange={setPageSize}
  labels={{
    rowsPerPage: 'Filas por página',
    page: 'Página',
    of: 'de',
  }}
/>

Server-Side Pagination

tsx
function ServerPaginatedTable() {
  const [pageIndex, setPageIndex] = useState(0)
  const [pageSize, setPageSize] = useState(10)

  const { data, isLoading } = useQuery({
    queryKey: ['items', pageIndex, pageSize],
    queryFn: () => fetchItems({ page: pageIndex, limit: pageSize }),
  })

  return (
    <>
      {isLoading ? <Spinner /> : <Table data={data.items} />}
      
      <TablePagination
        pageIndex={pageIndex}
        pageSize={pageSize}
        totalCount={data?.total}
        onPageChange={setPageIndex}
        onPageSizeChange={(size) => {
          setPageSize(size)
          setPageIndex(0)
        }}
      />
    </>
  )
}

Cursor-Based Pagination

For cursor-based pagination (no total count):

tsx
function CursorPaginatedTable() {
  const [pageIndex, setPageIndex] = useState(0)
  const [pageSize, setPageSize] = useState(10)

  const { data, hasNextPage, hasPreviousPage } = useCursorPagination({
    pageSize,
  })

  return (
    <>
      <Table data={data} />
      
      <TablePagination
        pageIndex={pageIndex}
        pageSize={pageSize}
        // No totalCount for cursor-based
        canPreviousPage={hasPreviousPage}
        canNextPage={hasNextPage}
        onPageChange={setPageIndex}
        onPageSizeChange={setPageSize}
      />
    </>
  )
}

Controlled Navigation

tsx
function ControlledPagination() {
  const [page, setPage] = useState(0)
  const [size, setSize] = useState(20)
  const totalPages = Math.ceil(totalCount / size)

  return (
    <TablePagination
      pageIndex={page}
      pageSize={size}
      totalCount={totalCount}
      canPreviousPage={page > 0}
      canNextPage={page < totalPages - 1}
      onPageChange={setPage}
      onPageSizeChange={(newSize) => {
        setSize(newSize)
        // Adjust page if current page would be out of bounds
        const newTotalPages = Math.ceil(totalCount / newSize)
        if (page >= newTotalPages) {
          setPage(newTotalPages - 1)
        }
      }}
    />
  )
}

Behavior

Page Index

  • 0-based: First page is 0, second page is 1, etc.
  • Automatically calculates total pages from totalCount and pageSize
  • Displays as 1-based to users (Page 1, Page 2, etc.)

Page Size Changes

When page size changes:

  1. Call onPageSizeChange with new size
  2. Typically reset to first page (pageIndex = 0)
  3. Recalculate total pages
tsx
onPageSizeChange={(newSize) => {
  setPageSize(newSize)
  setPageIndex(0) // Reset to first page
}}
  • Previous: Disabled when pageIndex === 0 or canPreviousPage === false
  • Next: Disabled when on last page or canNextPage === false
  • Last page calculated as: Math.ceil(totalCount / pageSize) - 1

Styling

The component uses Tailwind classes and can be customized:

tsx
<TablePagination
  className="mt-6 border-t pt-4"
  {...props}
/>

Accessibility

  • Previous/Next buttons have aria-label attributes
  • Page size select is properly labeled
  • Keyboard navigation supported

Integration with DataTable

TablePagination is used internally by DataTable:

tsx
// Inside DataTable component
<TablePagination
  pageIndex={table.getState().pagination.pageIndex}
  pageSize={table.getState().pagination.pageSize}
  totalCount={mode === 'client' ? data.length : totalCount}
  pageSizeOptions={pageSizeOptions}
  onPageChange={(pageIndex) => table.setPageIndex(pageIndex)}
  onPageSizeChange={(pageSize) => table.setPageSize(pageSize)}
  canPreviousPage={table.getCanPreviousPage()}
  canNextPage={table.getCanNextPage()}
/>

See Also