Styling guide
Keep the data engine. Own the interface.
Use the default theme, override its CSS variables, or opt out of visual chrome and apply Tailwind classes to semantic slots.
Tailwind-first chrome
unstyled leaves virtualization, sizing, keyboard behavior, and accessibility in place while removing the package visual preset.
<DataTable
unstyled
classNames={{
root: "overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm",
body: "bg-white",
expanderButton: ({ expanded }) =>
expanded ? "rounded bg-slate-900 text-white" : "rounded border border-slate-300",
expandedPanel: "border-y border-slate-200 bg-slate-50 p-5",
}}
dataSource={rows}
columnSettings={columns}
renderExpandedRow={({ row, collapse }) => (
<section className="grid gap-3 md:grid-cols-2">
<div><b>{row.name}</b><p>{row.description}</p></div>
<button onClick={collapse}>Close details</button>
</section>
)}
/>Expanded rows
The +/- gutter is immediately left of row selection. Several rows can remain open. Detail-panel JSX is measured with ResizeObserver, so images, async content, and responsive layouts update virtual row height automatically.
Use expandedRows and onExpandedRowsChange when expansion belongs in application state. Use isRowExpandable to omit the control for individual rows.
Default theme tokens
Pass typed tokens directly or set the same variables in CSS. Tokens cover color, typography, spacing, controls, focus, selection, scrollbars, overlays, editors, menus, and expanded panels.
<DataTable tokens={{
"--dt-bg": "#ffffff",
"--dt-cell": "#ffffff",
"--dt-surface": "#f8fafc",
"--dt-border": "#cbd5e1",
"--dt-border-subtle": "#e2e8f0",
"--dt-accent": "#4f46e5",
"--dt-expanded-bg": "#f8fafc",
"--dt-expanded-border": "#cbd5e1",
"--dt-expanded-padding-x": "1.25rem",
}} />.billing-table {
--dt-font: ui-sans-serif, system-ui;
--dt-header-bg: theme(colors.slate.50);
--dt-focus-ring: theme(colors.indigo.500);
--dt-menu-bg: theme(colors.white);
--dt-expanded-border: var(--dt-border);
}Semantic class slots
Slots target root chrome, toolbar/formula bar, headers, contextual cells, expansion, states, footer, scrollbars, editors, menus, dialogs, settings, validation, and notices. Row and cell slots accept resolver functions when they need row values.
classNames={{
dataCell: ({ row, colId }) => row.overdue && colId === "balance"
? "font-semibold text-red-700" : undefined,
expanderButton: ({ expanded }) => expanded ? "bg-slate-900 text-white" : "",
expandedPanel: ({ row }) => row.priority ? "border-amber-300 bg-amber-50" : "",
dialog: "rounded-xl border border-slate-200 bg-white shadow-2xl",
}}Portal surfaces
Use the menu, dialog, settings, validation, and notice slots for portaled UI. Keep theme tokens on those surfaces when mounting custom portals of your own.