Customization
A spectrum, all opt-in: restyle parts with classNames, replace parts with
slots, tune the chrome with props, or theme through your kit’s provider.
classNames — per-part styling
Section titled “classNames — per-part styling”Restyle without replacing. Mantine and Chakra expose five wrapper hooks
(root, toolbar, table, card, footer); the unstyled adapter
exposes a hook for every rendered node:
import { DataTable } from "@adapttable/unstyled";
<DataTable data={data} columns={columns} rowKey={(r) => r.id} classNames={{ table: "w-full text-sm", row: "border-b hover:bg-zinc-50 data-[selected]:bg-blue-50", cell: "px-3 py-2", filtersPopover: "rounded-lg border bg-white shadow-xl", filtersDone: "rounded-md bg-zinc-900 text-white px-3 py-2", }}/>;Every unstyled node also carries a stable data-adapttable-part attribute —
the kebab-case of the classNames key (searchField →
data-adapttable-part="search-field") — plus data-* state attributes, so
plain CSS, Tailwind, and shadcn tokens all work. The full part map:
Toolbar & search
Section titled “Toolbar & search”| Part | Element |
|---|---|
root | The outer wrapper around the whole table. |
toolbar | The toolbar row (search, filters, columns, views, your toolbar). |
searchField | The search field wrapper (input + leading icon). |
search | The search <input>. |
searchIcon | The leading magnifying-glass icon. |
sortSelect | The mobile sort-by <select>. |
rowsPerPageSelect | Rows-per-page <select> (toolbar in infinite mode, footer when paged). |
Filters
Section titled “Filters”| Part | Element |
|---|---|
filtersButton | The Filters trigger button. |
filtersIcon | The funnel icon inside the trigger. |
filtersCount | The active-filter count badge. |
filtersAnchor | The popover anchor wrapper around the trigger. |
filtersPopover | The anchored popover card (filtersMode="popover"). |
filtersBackdrop | The drawer backdrop (filtersMode="drawer"). |
filtersPanel | The drawer panel. |
filtersHeader / filtersTitle / filtersClose | Panel header, its title, and the close button. |
filtersBody / filtersFooter | The panel content area and its action row. |
filtersClear / filtersDone | The clear-all and done/apply buttons. |
filterField / filterLabel | One auto-built field’s wrapper and its caption. |
filterInput / filterSelect / filterOperator | Text/date/number inputs, the select widget, and a range field’s operator <select>. |
filterCheckboxGroup / filterCheckbox | A multiSelect checkbox list and one option. |
filterOptionsLoading | The placeholder shown while async options load. |
| Part | Element |
|---|---|
chips | The active-filter chip strip. |
chip | One removable chip. |
chipRemove | A chip’s remove button. |
Column menu & resize
Section titled “Column menu & resize”| Part | Element |
|---|---|
columnMenu / columnMenuButton / columnMenuPanel | The menu wrapper, trigger, and dropdown panel. |
columnMenuHeader / columnMenuTitle | The panel header and its title. |
columnMenuItem / columnMenuGrip / columnMenuLabel | One column row, its drag grip, and its label. |
columnMenuVisibility / columnMenuPin | The show/hide toggle and the pin control. |
columnMenuSeparator / columnMenuReset | The separator above the actions entry and the reset button. |
resizeHandle | A header’s drag/keyboard resize handle. |
Saved views
Section titled “Saved views”| Part | Element |
|---|---|
viewsButton / viewsPanel | The menu trigger and dropdown panel. |
viewsItem / viewsDelete | One view’s apply button and its delete button. |
viewsInput / viewsSave | The view-name input and the save button. |
Selection & bulk actions
Section titled “Selection & bulk actions”| Part | Element |
|---|---|
bulkBar / bulkButton | The selection toolbar and one bulk-action button. |
selectAllBanner / selectAllText / selectAllButton | The cross-page banner, its status text, and its action. |
selectionCell / checkbox | A row’s selection cell and the checkbox itself. |
| Part | Element |
|---|---|
table / thead / tbody | The <table> and its sections. |
headerRow / headerCell | The header <tr> and one <th>. |
groupRow / groupCell | The grouped-header row and one spanning cell. |
sortButton / sortIndex | A sortable header’s button and its multi-sort badge. |
row / cell | One body <tr> and one <td>. |
actionsCell / actionButton | The trailing actions cell and one action button. |
Row expansion
Section titled “Row expansion”| Part | Element |
|---|---|
expandHeader / expandCell | The leading chevron header cell and body cell. |
expandButton | The expand/collapse chevron (rows and cards). |
detailRow / detailCell | The full-width detail <tr> and its spanning <td>. |
cardDetail | The detail section inside an expanded mobile card. |
Mobile cards & summary
Section titled “Mobile cards & summary”| Part | Element |
|---|---|
cards / card | The card list and one card. |
cardRow / cardLabel / cardValue | One label/value line inside a card. |
summary / summaryRow / summaryCell | The <tfoot>, its <tr>, and one summary <td>. |
summaryCard | The trailing summary card in the mobile list. |
Footer, pagination & states
Section titled “Footer, pagination & states”| Part | Element |
|---|---|
footer / pageButton | The footer bar and the prev/next/numbered page buttons. |
loadMore / loadMoreButton | The infinite-mode sentinel area and its button. |
empty / emptyClear | The empty state and its clear-filters button. |
loading / refreshIndicator | The first-load skeleton and the background-refresh bar. |
error / retryButton | The error state and its retry button. |
A few purely structural nodes (scroll-box, virtual-spacer, the skeleton
internals) expose only the data-adapttable-part attribute.
Replace whole sub-components on any adapter:
<DataTable data={data} columns={columns} rowKey={(r) => r.id} slots={{ empty: <MyEmptyState />, skeleton: <MySkeleton /> }}/>skeleton replaces the first-load skeleton; empty replaces the
empty-state. The error state is built-in (retry button included) — translate it
via the errorTitle / errorMessage / retry labels. The unstyled adapter
also accepts the equivalent top-level emptyState / loadingState props
(the slots entry wins when both are set).
Density
Section titled “Density”<DataTable data={data} columns={columns} rowKey={(r) => r.id} density="compact"/>"comfortable" (default) is the roomy layout; "compact" tightens row
height and padding. Each adapter maps it to its kit’s table size — MUI
"comfortable" → medium / "compact" → small, antd → middle /
small — and MUI, Chakra, and antd offer an explicit size prop that
overrides the mapping (e.g. antd size="large").
Sticky header, offset & scroll box
Section titled “Sticky header, offset & scroll box”<DataTable data={data} columns={columns} rowKey={(r) => r.id} stickyHeader // keep the desktop header pinned while the page scrolls stickyTop={56} // offset under your app header (also offsets the toolbar) maxHeight={420} // fixed-height scroll box instead of page scroll/>maxHeight turns the table into a scroll box that also scrolls sideways —
the header and pinned columns stick within it, which is what makes column
pinning visibly stick. scrollToTopOnChange (default true) scrolls back
to the table when search/filter/page changes, with scrollTopGap (default
8) of breathing room below sticky chrome.
Theming per kit
Section titled “Theming per kit”The core is style-free: wrap your app in the kit’s provider and AdaptTable
renders with that kit’s real components, following its theme and dark mode
(prefers-color-scheme) automatically. Kit-specific knobs:
- Mantine —
animateenables a dependency-free row/card entrance stagger (honours reduced motion). Rolling your own? Every animatable row/card carries adata-staggerattribute — leaveanimateoff and target those elements with GSAP/Framer Motion. - MUI —
size("small" | "medium") overrides the density mapping;classNamelands on the root<Paper>. - Chakra —
colorSchemecolors primary accents (buttons, badges);size("sm" | "md" | "lg", default"md"). - Ant Design —
size("small" | "middle" | "large"),borderedfor cell borders,virtualHeight/virtualWidthfor the virtualized scroll area,classNameon the wrapper. - Unstyled — no provider needed; theme entirely through
classNames, thedata-adapttable-parthooks above, and your own CSS variables /data-theme.
Custom cells
Section titled “Custom cells”Pass a Cell component (receives { row, rowIndex }; define it at module
level so its identity is stable) or a lighter accessor:
import type { CellProps } from "@adapttable/core";
function StatusCell({ row }: CellProps<Person>) { return ( <Badge color={row.status === "active" ? "green" : "gray"}> {row.status} </Badge> );}
const columns = [ { key: "name", sortable: true }, { key: "status", Cell: StatusCell }, { key: "salary", accessor: (r: Person) => formatMoney(r.salary), align: "end", },];See the Columns guide and the
ColumnDef table for the full column surface
(sortValue, i18n, group, hideOnMobile, …).