---
title: useTokenPagination
description: Forward-only token-based pagination that bridges gRPC/Connect APIs to TanStack Table.
icon: arrow-right-to-line
---

<PoweredBy packages={[
  { name: '@tanstack/react-table', url: 'https://tanstack.com/table' },
]} />

### Installation

```package-install
npx shadcn@latest add @redpanda/use-token-pagination
```

`useTokenPagination` adapts **token-based** pagination (the `nextPageToken` model used by gRPC/Connect APIs) to TanStack Table's offset-based `pagination` state. It's **forward-only**: there is no page token for going back, so `canPreviousPage` is always `false`. After each fetch, report the next token via `onPageResolved`, and wire `paginationState` / `onPaginationChange` into `<DataTable>`.

:::warning
Pass `tableOptions={{ manualPagination: true }}` to `<DataTable>` so TanStack Table doesn't render back-navigation controls this hook can't satisfy. Provide a next-only pagination UI.
:::

## Usage

```tsx showLineNumbers
import { useTokenPagination } from "@/hooks/use-token-pagination";
import { DataTable } from "@/components/redpanda-ui/data-table";

function TokenPaginatedTable() {
  const pagination = useTokenPagination({ pageSize: 20 });

  const { data } = useQuery({
    queryKey: ["rows", pagination.currentPageToken, pagination.pageSize],
    queryFn: async () => {
      const result = await fetchRows({
        pageToken: pagination.currentPageToken,
        pageSize: pagination.pageSize,
      });
      pagination.onPageResolved(result); // report result.nextPageToken
      return result;
    },
  });

  return (
    <DataTable
      columns={columns}
      data={data?.rows ?? []}
      tableOptions={{
        state: { pagination: pagination.paginationState },
        manualPagination: true,
        pageCount: pagination.pageCount,
        onPaginationChange: pagination.onPaginationChange,
      }}
    />
  );
}
```

## API

### `useTokenPagination({ pageSize? })`

| Option     | Type     | Default | Description                        |
| ---------- | -------- | ------- | ---------------------------------- |
| `pageSize` | `number` | `10`    | Initial rows per page.             |

**Returns**

| Property             | Type                          | Description                                                        |
| -------------------- | ----------------------------- | ------------------------------------------------------------------ |
| `currentPageToken`   | `string \| undefined`         | Token for the current fetch; `undefined` on page 0.                |
| `pageSize`           | `number`                      | Current page size.                                                 |
| `onPageResolved`     | `(r: { nextPageToken?: string }) => void` | Call after each fetch to record the next token.        |
| `paginationState`    | `PaginationState`             | Pass to `tableOptions.state.pagination`.                            |
| `onPaginationChange` | `OnChangeFn<PaginationState>` | Pass to `tableOptions.onPaginationChange`.                          |
| `pageCount`          | `number`                      | Page count derived from `hasNextPage`.                             |
| `canNextPage`        | `boolean`                     | Whether a next page is available.                                  |
| `canPreviousPage`    | `false`                       | Always `false` — backward navigation is unsupported.              |
| `reset`              | `() => void`                  | Reset to page 0.                                                   |

## Related

For offset/URL-driven pagination, see [`useDataTableSearchParams`](/docs/use-data-table-search-params).
