Skip to content

Migrate from TanStack Table

TanStack Table is a superb headless engine — you keep total control because it renders nothing: no markup, no toolbar, no filter inputs, no pagination controls, no URL sync. AdaptTable shares that philosophy. @adapttable/core is also headless (prop-getters, no forced markup), but it comes with the parts you rebuild on every TanStack project already wired: a filter UI, a toolbar, pagination, URL-synced state, and saved views. And when you don’t need bespoke markup, an adapter renders native kit components so you can delete the table UI entirely.

TanStack is multi-framework (React, Vue, Svelte, Solid, …); AdaptTable is React-only. If you’re not on React, stay on TanStack. This page is for React teams tired of rebuilding the same chrome.

Everything TanStack leaves to you (verified from its docs), done for you:

  • A filter UI. TanStack gives you columnFilters state and getFacetedUniqueValues, but you render every input. AdaptTable derives kit-native widgets, removable chips, and URL params from a filter declaration. See filtering.
  • A toolbar, pagination, and search — none of which exist in TanStack; you wire the buttons around nextPage()/setPageIndex() yourself.
  • URL-synced state. TanStack has no URL API; you serialize sorting/columnFilters/pagination by hand. AdaptTable does it. See URL state.
  • Saved views — no concept in TanStack. See saved views.
  • No opt-in row models. You don’t import getSortedRowModel / getFilteredRowModel / getPaginationRowModel — search, filter, sort, and paging run by default on the frontend tier.
  • Native kit UI, optional. Keep headless control with useDataTable prop-getters, or adopt an adapter (@adapttable/mantine, mui, …) and drop your hand-written markup.

Headless core only, or core plus an adapter for ready UI:

Terminal window
# Headless (keep rendering your own markup, TanStack-style)
pnpm add @adapttable/core
# Batteries — native components for your kit (delete the markup)
pnpm add @adapttable/core @adapttable/mantine @mantine/core @mantine/hooks
TanStack TableAdaptTableNotes
useReactTable({ data, columns, getCoreRowModel })useDataTable({ source, columns }) or an adapter <DataTable>source from useFrontendData; adapters accept data directly.
getSortedRowModel / getFilteredRowModel / getPaginationRowModel(none needed)Applied automatically on the frontend tier.
flexRender(cell.column.columnDef.cell, …)adapter renders, or prop-getters (getCellProps, …)Headless route mirrors TanStack’s model.
columnHelper.accessor("name", …){ key: "name" }Dot paths work in both.
columnHelper.accessor(row => …) (accessorFn){ key, accessor: (row) => … }
columnHelper.display({ cell }){ key, Cell }Cell receives { row, rowIndex }.
header / cell / footerheader / Cell (or accessor) / summaryRowFooter is one table-level function.
enableSorting / state.sorting / onSortingChangesortable per column (+ multiSort)AdaptTable owns sort state.
columnFilters + getFilteredRowModel + your inputscolumn filter shorthand + filters arrayWidgets + chips built for you.
state.pagination + getPaginationRowModel + your controlsautomatic paginationPaged on desktop, infinite on mobile.
enableRowSelection + state.rowSelection + your checkboxesbulkActions / selectedIds / onSelectionChange
getExpandedRowModel + row.getToggleExpandedHandler()renderRowDetailToggle UI is provided, not hand-added.
columnVisibility / columnOrder / columnPinning stateenableColumnMenu / columnLayoutOne built-in menu instead of hand-wired state + DnD.
manualSorting / manualFiltering / manualPaginationonQueryChange (or source via useBackendData)One consolidated server query. See data tiers.
(serialize state to the URL yourself)urlSync / urlKey / savedViewsOn by default.

Before — TanStack: opt-in row models, flexRender, and hand-built pagination:

import {
createColumnHelper,
flexRender,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
const col = createColumnHelper<Person>();
const columns = [
col.accessor("name", { header: "Name" }),
col.accessor("role", { header: "Role" }),
];
function PeopleTable({ people }: { people: Person[] }) {
const table = useReactTable({
data: people,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
return (
<>
<table>
<thead>
{table.getHeaderGroups().map((hg) => (
<tr key={hg.id}>
{hg.headers.map((h) => (
<th key={h.id} onClick={h.column.getToggleSortingHandler()}>
{flexRender(h.column.columnDef.header, h.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
{/* …and you still hand-write the pagination buttons, search box,
filter inputs, and URL syncing. */}
</>
);
}

After — an adapter renders native UI; search, sort, filters, pagination, and URL state come built in:

import { DataTable } from "@adapttable/mantine"; // or mui, chakra, antd, …
function PeopleTable({ people }: { people: Person[] }) {
return (
<DataTable
data={people}
rowKey={(r) => r.id}
columns={[
{ key: "name", sortable: true },
{ key: "role", filter: { type: "select", options: "auto" } },
]}
/>
);
}

Prefer to keep your own markup? Stay headless with @adapttable/core: useFrontendData for the source and useDataTable for the prop-getters (getTableProps, getHeaderCellProps, getSortButtonProps, getRowProps, getCellProps, getSearchInputProps) — the same headless shape as TanStack, but with filters, pagination, and URL state already handled.

  • React only. On Vue/Svelte/Solid/Angular, keep TanStack — AdaptTable has no adapter for those.
  • rowKey is required (AdaptTable’s getRowId).
  • Delete the opt-in row models. No getSortedRowModel / getFilteredRowModel / getPaginationRowModel imports — those run for you.
  • Filters are declarative, not hand-rendered. Swap your custom filter inputs for a filter shorthand; a bespoke control can still be JSX in filters.
  • You keep the headless escape hatch. useDataTable returns prop-getters, so moving to AdaptTable doesn’t mean giving up control — it means not rebuilding the chrome first.