Skip to main content

Theming

The grid themes like a MUI component, and your MUI theme is the single source of truth — set colours once and the grid follows. The override surface mirrors MUI X DataGrid.

1. Inherits your MUI theme

Out of the box, colours, typography, spacing, radius, dark mode, and RTL all come from your app's ThemeProvider. No setup needed.

2. Theme it like a MUI component

Register palette.tanstackDataGrid and components.MuiTanstackDataGrid in createTheme — exactly like MuiButton or MuiDataGrid:

import { createTheme } from '@mui/material/styles';

const theme = createTheme({
// colours once, in the palette (defaults derive from the base palette)
palette: {
tanstackDataGrid: { headerBg: '#eef2ff', headerColor: '#4338ca', borderColor: '#e0e7ff' },
},
components: {
MuiTanstackDataGrid: {
defaultProps: { density: 'compact', striped: true },
styleOverrides: {
header: { textTransform: 'uppercase', letterSpacing: '0.04em', fontSize: 11 },
cell: { borderRight: '1px solid var(--dt-border-color)' },
row: { '&:hover': { '--dt-row-bg': '#f5f3ff' } },
},
},
},
});

The grid below is the same one, rendered under that theme:

Loading demo…

3. Design tokens (CSS variables)

Every visual is a --dt-* CSS variable whose default derives from your MUI theme. Override a token only to make the grid deviate from the rest of the app.

The grid renders as a self-framing card — a 1px --dt-border-color outer border with --dt-radius rounded corners — so you don't need to wrap it in your own <Paper>. Drop the frame with sx={{ border: 0, borderRadius: 0 }} on the root if you prefer.

TokenDefaults from
--dt-border-colorpalette.divider
--dt-header-bgpalette.grey[50] (light) / palette.grey[900] (dark)
--dt-header-colorpalette.text.secondary
--dt-row-bg-hoverpalette.action.hover
--dt-row-bg-selectedpalette.action.selected
--dt-bulkbar-bg / --dt-bulkbar-fgpalette.primary.main / palette.primary.contrastText (the selection bar)
--dt-row-bg-stripesubtle tint (used when striped)
--dt-row-height, --dt-cell-padding-x/ydensity preset
--dt-font-sizedensity preset
--dt-radiustheme.shape.borderRadius (floored at 8px)

4. Per-instance overrides

Style one grid without touching the theme: the density prop, sx on the root, or slotProps on any part. sx and every slotProps entry merge with the built-in styling (yours wins per-property) — you never lose the grid's roles, refs, keyboard wiring, or --dt-* tokens, and you never manage colours twice.

<DataTable
columns={columns}
data={rows}
// Root sx composes with the theme + tokens — e.g. drop the card frame just here:
sx={{ border: 0, borderRadius: 0 }}
slotProps={{
// Inject sx/props into any part; the built-in styles stay underneath.
row: { sx: { '&:nth-of-type(even)': { bgcolor: 'action.hover' } } },
cell: { sx: { fontVariantNumeric: 'tabular-nums' } },
// Leaf controls (pagination, toolbar buttons) take your props last, so overrides win:
pagination: { rowsPerPageOptions: [10, 25, 100] },
}}
/>

To replace a part outright instead of styling it, pass a component to slots.<key> (slots={{ noRowsOverlay: MyEmptyState }}).

5. Dark mode

The grid follows your theme's light/dark mode automatically — including the modern setup createTheme({ cssVariables: true, colorSchemes: { light, dark } }). It reads palette values via theme.vars and emits its mode-dependent defaults with applyStyles('dark'), so toggling the scheme restyles the header, rows, borders, and toolbar:

Loading demo…
const theme = createTheme({
cssVariables: { colorSchemeSelector: 'class' },
colorSchemes: { light: true, dark: true },
});
// …toggle with useColorScheme().setMode('dark') — the grid follows.

To customise grid colours per scheme, merge createDataTableTheme (palette + optional darkPalette) — it emits colorSchemes overrides that compose cleanly with a cssVariables theme (no flat top-level palette).

6. Right-to-left (RTL)

Set direction: 'rtl' on your theme and the grid mirrors itself — no extra setup. Pinned columns flip side, the resize handle and sort/⋮-menu move to the logical edge, text aligns to the start, and tree indentation and the column separators mirror. The grid sets dir="rtl" on its root, so it works even without a dir attribute on a parent.

Loading demo…
import { ThemeProvider, createTheme } from '@mui/material/styles';

<ThemeProvider theme={(outer) => createTheme(outer, { direction: 'rtl' })}>
<DataTable columns={columns} data={data} enableColumnPinning enableColumnResizing />
</ThemeProvider>