react-datatable
Reference

Component API

Use this page when you already know which capability you want and need the exact top-level prop or export surface. For implementation guidance, start with the rewritten guides and concepts pages.

Public imports

import {
  Datatable,
  DataTableProvider,
  useDatatableColumns,
  useDatatableColumnSizing,
  useDatatableStore,
  useDatatableStoreApi,
  type DatatableColumn,
  type DatatableProps,
} from "./react-datatable"
  • Datatable is the component export.
  • DatatableProps is the component prop type.
  • DataTableProvider and store hooks are lower-level exports for advanced integrations around the shipped table state model.

Main component contract

interface DatatableProps<TData> {
  tableKey: string
  columns: DatatableColumn<TData>[]
  getRowId?: (row: TData) => string
  initialState?: Partial<DatatableState>
  runtimeRestoration?: boolean | { key?: string; pagination?: boolean; scroll?: boolean }
  onReadyStateChange?: (ready: boolean) => void
  rowHeight?: number
  headerHeight?: number
  virtualization?: DataTableVirtualizationConfig
  columnReordering?: DataTableColumnReorderingConfig
  selection?: DataTableRowSelectionConfig<TData>
  bulkActions?: DataTableBulkActionsConfig<TData>
  rowPresentation?: DataTableRowPresentationConfig<TData>
  keyboardNavigation?: DataTableKeyboardNavigationConfig
  rowActions?: DataTableRowActionsConfig<TData>
  preview?: DataTableRowPreviewConfig<TData>
  enableColumnResizing?: boolean
  columnResizeMode?: "onChange" | "onEnd"
  persistState?: { ... }
  urlSync?: boolean | Omit<UseDatatableUrlOptions<TData>, "columns">
  stickyColumnsCount?: number
  columnVisibilityUI?: ColumnVisibilityUIConfig
  displayOptions?: boolean | DataTableDisplayOptionsConfig
  views?: { ... }
  toolbar?: boolean | { ... }
  viewColumnsButton?: boolean | { ... }
  debug?: boolean
}

type DatatableLocalModeProps<TData> = {
  data: TData[]
  online?: never
}

type DatatableOnlineModeProps<TData> = {
  online: OnlineConfig<TData>
  data?: never
}

Required setup

tableKey

tableKey is required. Use a stable product-surface key such as "customers", "deals", or "workspace-members". The table uses it as the default key for runtime restoration, persistence, saved views, and floating preview position.

columns

columns is required. The table uses this model for rendering, sorting, filtering, grouping, visibility, and customization seams.

See Column API for the full column shape.

data or online

Provide exactly one primary data mode:

  • data: local/offline mode. The table owns filtering, sorting, grouping, and viewport rendering against the array you pass in.
  • online: server-driven mode. The table owns interaction state, but your query function owns row retrieval, totals, facets, and backend ordering.

If the first online response is already loaded by a route loader or parent cache, pass it through online.initialData. Top-level data is only for local mode.

If you provide neither, the component throws during render.

See:

getRowId

Use getRowId when your row key is not the default TanStack row index. In practice you should provide it for any real product table so selection, preview, persistence, and online transitions stay stable.

Initialization and readiness

initialState

<Datatable
  tableKey="customers"
  initialState={{
    sorting: [{ id: "updatedAt", desc: true }],
    globalFilter: "active",
    grouping: ["status"],
  }}
/>

initialState seeds the store before saved views, persisted state, and URL state are merged in. Treat it as your developer default, not as a forced final state.

See State Lifecycle.

onReadyStateChange

onReadyStateChange(ready) fires when the table has finished loading coordinated state sources and the interactive surface is ready.

Use it when surrounding UI must wait for:

  • persisted current-view state
  • default saved views
  • initial URL state
  • initial online loading

runtimeRestoration

Runtime restoration keeps navigation position available while the current browser session is alive. It is useful in SPAs where a table can unmount and remount as users switch tabs, open a row detail route, or move between sibling screens.

It is enabled by default and uses the top-level tableKey.

<Datatable
  tableKey="customers"
  runtimeRestoration={{
    pagination: true,
    scroll: true,
  }}
/>

The available shape is:

boolean | {
  key?: string
  pagination?: boolean
  scroll?: boolean
}

Use runtimeRestoration={false} when route changes should always return the table to its initial runtime position. Use { pagination: false } when page movement should reset on remount but scroll should still come back, or { scroll: false } when page state should return but scroll should start at the top.

This is separate from persistState. Runtime pagination and scroll position use sessionStorage by default so they survive remounts and hard refreshes in the same browser tab. Persisted state is durable user preference storage.

Sizing and layout

rowHeight

Height of data rows.

  • default: 40

headerHeight

Height of header rows.

  • default: 40

stickyColumnsCount

Initial number of left-frozen columns. Users can still change frozen columns later if you expose the relevant display options controls.

enableColumnResizing

Enables header drag resizing.

  • default: true

columnResizeMode

"onChange" | "onEnd"
  • onChange: resize continuously while dragging
  • onEnd: update width after drag ends

Feature configuration groups

The top-level API is intentionally grouped by reader job. Most capabilities live under one focused prop object.

<Datatable
  tableKey="customers"
  virtualization={{ ... }}
  columnReordering={{ ... }}
  selection={{ ... }}
  bulkActions={{ ... }}
  rowPresentation={{ ... }}
  keyboardNavigation={{ ... }}
  rowActions={{ ... }}
  preview={{ ... }}
  persistState={{ ... }}
  urlSync={{ ... }}
  views={{ ... }}
  toolbar={{ ... }}
  displayOptions={{ ... }}
  viewColumnsButton={{ ... }}
/>

virtualization

Controls viewport rendering policy and overscan.

{
  mode?: "viewport" | "full"
  rowOverscanCount?: number
  columnOverscanCount?: number
}

Use this for render-cost tuning, not data fetching.

See Tune virtualization.

columnReordering

Controls drag behavior for visible columns.

{
  allowFrozenBoundaryCrossing?: boolean
  widthMorph?: "none" | "snap" | "interpolate"
}

See Add column ordering.

selection

Enables row selection.

{
  enabled: boolean
  mode?: "multi"
  showCheckboxOnHover?: boolean
  maxSelectedRows?: number
  allowSelectAllMatching?: boolean
  getRowCanSelect?: (row) => boolean
}

See Add row selection.

bulkActions

Configures the selected-row action flow.

{
  actions: DataTableBulkAction<TData>[]
  triggerLabel?: string
  serverExecutor?: (request: DataTableBulkServerActionRequest) => void | Promise<void>
}

See Add bulk actions.

rowPresentation

Adds state-aware row and cell styling hooks.

Use it when selection, active-row, or preview state should change classes or attributes without replacing the underlying table behavior.

See Styling rows and cells.

keyboardNavigation

{
  enabled?: boolean
  autoFocus?: boolean
}

Enables active-row keyboard movement and related Enter/Space behavior.

See Add keyboard navigation.

rowActions

{
  onOpenRow?: (context) => void | Promise<void>
  onTogglePreviewRow?: (context) => void | Promise<void>
}

Use rowActions when the table should signal row intent but your app owns the actual route navigation or domain-specific side effect.

preview

{
  enabled?: boolean
  floating?: {
    draggable?: boolean
    storageKey?: string
  }
  renderPreview: ({ row, rowId, close }) => ReactNode
}

Adds the built-in preview surface for the current preview row.

See:

Persistence, sharing, and URL state

persistState

Use persistState for the current user's remembered table state.

<Datatable
  tableKey="customers"
  persistState={{
    adapter: localStorageAdapter,
    workspaceId: "wsp_123",
    userId: "usr_456",
    debounceMs: 1000,
    onSave: () => {},
    onError: (error) => console.error(error),
  }}
/>

Required fields:

  • adapter

Optional fields:

  • tableKey when this adapter should use a namespace different from the top-level table key
  • workspaceId
  • userId
  • debounceMs
  • onSave
  • onError

See Add current-view persistence.

urlSync

boolean | {
  enabled?: boolean
  acceptUrlParams?: boolean
  historyMode?: "push" | "replace"
  ...
}
  • false: do not write ongoing state to the URL, but still accept incoming URL params by default
  • true: enable continuous bidirectional URL synchronization
  • object: configure the URL behavior explicitly

Only one table per page should actively write to the shared query param space.

See Add URL sync.

views

Use views for named saved views.

<Datatable
  tableKey="customers"
  views={{
    adapter,
    workspaceId: "wsp_123",
    userId: "usr_456",
    enableWorkspaceSharing: true,
    enableUserDefaults: true,
    onSuccess: () => {},
    onError: (error) => console.error(error),
  }}
  toolbar={{ views: true }}
/>

Required fields:

  • adapter

Optional fields:

  • tableKey when this adapter should use a namespace different from the top-level table key
  • workspaceId
  • userId
  • enableWorkspaceSharing
  • enableUserDefaults
  • onSuccess
  • onError

If views is configured but toolbar.views is hidden, the saved-view data model still exists but the shipped toolbar entry point does not render.

See:

Toolbar and display surfaces

toolbar

boolean | {
  quickSearch?: boolean | {
    placeholder?: string
    debounceMs?: number
  }
  filterButton?: boolean
  displayOptions?: boolean
  copyLink?: boolean
  views?: boolean
  appliedState?: {
    showSorting?: boolean
    showFilters?: boolean
  }
}

Use this to choose which shipped toolbar controls appear. It does not replace the underlying feature contracts; it exposes or hides the surface.

See:

displayOptions

boolean | {
  sections?: {
    grouping?: boolean
    ordering?: boolean
    freezeColumns?: boolean
    displaySettings?: boolean
    columnVisibility?: boolean
  }
  controls?: {
    showEmptyGroups?: boolean
    showOrderingBadge?: boolean
  }
}

false hides the display options button completely. An object keeps the button but limits what users can control.

columnVisibilityUI

Controls how column visibility management renders.

  • badges
  • dropdown
  • auto

viewColumnsButton

boolean | {
  show?: boolean
  width?: number
}

Controls the optional right-side view-columns affordance.

Lower-level exports

DataTableProvider

Creates the isolated store and coordinates initialState, persistence, saved views, and URL state before the table body renders.

Use it only when you are building a more custom wrapper around the shipped table internals.

useDatatableColumns

Returns column metadata from provider context for internal-style integrations that need the current public column model.

useDatatableColumnSizing

Lower-level sizing hook exported from DataTableBody.

useDatatableStore and useDatatableStoreApi

Advanced hooks into the Zustand-backed table store.

Reach for these only when prop-level APIs are not enough and you are intentionally extending the source-owned table behavior.

On this page