SuperAPI AI Directory

tanstack-table-core-client-vs-server

Frontend & websites

View repository

Updated 10-16 00:02

About this skill

This skill builds on core and table-features. Read them first for the model pipeline and plugin slots.

Setup

import {
  rowPaginationFeature,
  rowSortingFeature,
  tableFeatures,
} from '@tanstack/table-core'

export const features = tableFeatures({ rowSortingFeature, rowPaginationFeature, }) export const serverOptions = { manualSorting: true, manualPagination: true, rowCount: 12_450, } as const


## Core Patterns

### Client owns the complete pipeline

```ts
const features = tableFeatures({
  columnFilteringFeature,
  filteredRowModel: createFilteredRowModel(),
  rowSortingFeature,
  sortedRowModel: createSortedRowModel(),
  rowPaginationFeature,
  paginatedRowModel: createPaginatedRowModel(),
})

Pass the full client dataset so each stage can process all rows.

Server owns processing

const options = {
  data: serverPage.rows,
  manualFiltering: true,
  manualSorting: true,
  manualPagination: true,
  rowCount: serverPage.total,
}

Put controlled filter, sorting, and pagination state into the request/query key.

Common Mistakes

[CRITICAL] Treating manual flags as requests

Wrong:

const options = { manualSorting: true, data: unsortedRows }

Correct:

const sortedPage = await fetchPage({ sorting })
const options = { manualSorting: true, data: sortedPage.rows }

manualSorting only bypasses client sorting; it never calls a backend.

Source: packages/table-core/src/features/row-sorting/rowSortingFeature.types.ts

[HIGH] Expecting manual pagination to slice

Wrong:

const options = { data: allRows, manualPagination: true }

Correct:

const page = await fetchPage({ pageIndex, pageSize })
const options = {
  data: page.rows,
  manualPagination: true,
  rowCount: page.rowCount,
}

Manual pagination assumes data already represents the intended page. Pass the stable page result directly. If the application deliberately performs custom local slicing instead, derive it with the framework's memo/computed primitive and keep the reference stable until its actual inputs change.

Source: docs/framework/react/guide/pagination.md#manual-server-side-pagination

[HIGH] Sorting only the loaded page accidentally

Wrong:

const options = {
  data: serverPage.rows,
  manualPagination: true,
  manualSorting: false,
}

Correct:

const options = {
  data: serverPage.rows,
  manualPagination: true,
  manualSorting: true,
}

Client sorting can see only loaded rows, so it cannot produce database-wide order.

Source: docs/guide/row-models.md

[HIGH] Recreating processed data or columns inline

Wrong:

const options = {
  data: allRows.filter(matchesFilters).slice(pageStart, pageEnd),
  columns: makeColumns(),
}

Correct:

const processedRows = pageResult.rows
const columns = sharedColumns
const options = { data: processedRows, columns }

data and columns are model inputs. Keep both references stable between meaningful changes with module constants, state, or the adapter's memo/computed primitive; otherwise Table repeatedly invalidates row and column models and some adapters can enter render loops. “Manual” describes processing ownership, not permission to derive new arrays inside table options.

Source: docs/guide/data.md

API Discovery

Inspect node_modules/@tanstack/table-core/dist/core/row-models/coreRowModelsFeature.utils.d.ts for pipeline order and each feature's .types.d.ts for its manual* contract.