Headless engine · native UI-kit adapters

One headless engine.
Every UI kit.

Headless freedom, batteries included for your kit. The same table features render natively in Mantine, MUI, Chakra, Ant Design, shadcn/Tailwind — or your own custom UI. URL state, RTL, and a real filter UX, out of the box.

npx @adapttable/cli init
Try the live demo
Renders natively in Mantine MUI Chakra Ant Design shadcn / Tailwind Custom Tailwind
The batteries

Everything a data table owes you.

Columns & filters

One declaration drives the widget, the URL params, the chips and the row predicate. Operator-first number and date filters: Equal, At least, At most, Between.

Three data tiers

Hand the table an array and it filters, sorts and pages in memory. Or own the fetch: one consolidated query event with an abort signal. Or bring a full custom TableSource.

URL-synced everything

Search, sort, filters, pagination, column layout and saved views live in shareable URLs — namespaced per table, SSR-safe, with a one-prop opt-out (urlSync={false}).

50,000 rows, no flinching

Opt-in virtualization windows the DOM against the page or any max-height scroll box. The live demo scrolls fifty thousand rows with a handful of DOM nodes.

i18n & RTL, for real

Label presets for ten locales. Columns map per-locale data paths, so cells, sorting and filtering follow the language. Pinning and layout are logical — Arabic and Hebrew just work.

Column management

Show, hide, reorder, pin and resize — the row-actions column included. Capture any state as a named saved view and restore it later, per table.

Selection & bulk actions

Select the page — or all matches across every page. Bulk actions run through an injectable confirm, and per-row actions explain themselves with disabledReason.

Accessible by default

Real table roles, aria-sort, focus management and full keyboard navigation — column reorder and resize included, straight from the keyboard.

Customization spectrum

Easy when you want. Headless when you need.

Start with props and grow all the way to prop-getters — at no point do you hit a wall and have to eject.

Customization guide
01

Props

Pass data + columns. Sensible defaults do the rest.

<DataTable source={src}
  columns={cols} />
02

Slots

Swap the empty, loading, and skeleton states.

slots={{ empty: <NoResults/>,
  skeleton: <Shimmer/> }}
03

classNames + data-*

Style any part; target rows by state.

classNames={{ row: 'hover:bg…' }}
// [data-selected] [data-pinned]
04

Custom toolbar

Inject your own toolbar + confirm dialog.

renderToolbar={(api) => …}
confirm={myConfirm}
05

Fully headless

Prop-getters. You own every element.

const { getRowProps,
  getHeaderProps } = useTable()
Easy Pro
Get started

Five lines to your first table.

The CLI detects your UI kit and scaffolds a working table; the docs cover every prop, the three data tiers, theming, i18n/RTL and the TanStack comparison.

import { DataTable } from "@adapttable/mantine"; // or mui, chakra, antd, unstyled

<DataTable
  data={people}
  columns={[
    { key: "name" },
    { key: "team", filter: "multiSelect" },
    { key: "hiredAt", filter: "dateRange" },
  ]}
/>
npx @adapttable/cli init