Migrate from MUI X DataGrid — Pro free (MIT)
▶ See it before you install: the live demo running on real Material UI — same components you already use, nothing to set up.
MUI X DataGrid is an excellent grid if
you are all-in on Material UI. The catch is its licensing: many everyday table
features live in the paid Pro and Premium tiers. @adapttable/mui
renders real MUI components — so it looks like Material UI — while giving you a
lot of that paid surface free under MIT, plus shareable URL state and an
automatic mobile layout the DataGrid doesn’t ship at any tier.
This page maps <DataGrid> to <DataTable> and shows a before/after.
What you gain (free, MIT)
Section titled “What you gain (free, MIT)”Features that are paid in MUI X but built into @adapttable/mui:
- Column pinning (Pro) → AdaptTable column layout / Columns menu.
- Row virtualization (Pro in v8) →
virtualize. Community<DataGrid>also caps pages at 100 rows; AdaptTable has no such cap. - Multi-column sort (Pro) →
multiSort. - Multiple simultaneous filters (Pro) → declarative
filters, always multi-condition. - Column resizing (Pro) →
resizableColumns. - Master-detail / detail panel (Pro) →
renderRowDetail. - Multiple row selection (Pro) →
bulkActions/selectedIds. - Footer summary rows →
summaryRow(MUI’s statistical aggregation is Premium; AdaptTable gives you the footer row, you supply the values).
Plus things DataGrid has no built-in answer for at any tier:
- URL-synced, shareable state — see URL state.
- An automatic responsive card layout on mobile (DataGrid’s List view is Pro and manual).
- One API for seven UI kits — the same code renders in Mantine, Chakra, Ant Design, Radix, Base UI, and shadcn/ui too.
Tiers above reflect MUI X v8 (2025), verified against MUI’s licensing docs. MUI may move features between tiers — check their licensing page before relying on a specific boundary.
Install
Section titled “Install”pnpm add @adapttable/core @adapttable/mui @mui/materialWorks with the default theme; wrap in <ThemeProvider> to customize, exactly as
DataGrid does.
Prop mapping
Section titled “Prop mapping”<DataGrid> → <DataTable>:
| MUI X DataGrid | @adapttable/mui |
Notes |
|---|---|---|
rows |
data |
Frontend tier. Server tier: data (page) + total + onQueryChange. |
columns |
columns |
GridColDef → ColumnDef, mapped below. |
getRowId |
rowKey |
Required — (row) => string. |
sortModel / onSortModelChange |
sortable per column (+ multiSort) |
AdaptTable owns and applies sort state. |
sortingMode: "server" |
onQueryChange (or source) |
The consolidated query carries sortBy/sortDir. |
filterModel / onFilterModelChange |
column filter shorthand + table filters |
Widgets, chips, and URL params are derived for you. |
paginationModel / onPaginationModelChange |
automatic (frontend) or total + onQueryChange |
No 100-row community cap. |
checkboxSelection + rowSelectionModel |
bulkActions, or selectedIds / onSelectionChange |
Multi-row selection is free; no { type, ids } model to manage. |
getDetailPanelContent (Pro) |
renderRowDetail |
(row) => ReactNode. |
loading |
loading |
— |
slots.noRowsOverlay / loadingOverlay |
slots.empty / slots.skeleton |
— |
initialState / apiRef.exportState() (state only) |
urlSync + urlKey (+ savedViews) |
State lives in the URL, shareable and reload-safe. |
pinnedColumns (Pro) / columnVisibilityModel |
enableColumnMenu / columnLayout |
Show/hide, reorder, pin in one menu. |
GridColDef → ColumnDef:
| MUI X DataGrid | @adapttable/mui |
Notes |
|---|---|---|
field |
key |
Also the value path (dot paths supported). |
headerName |
header |
Auto-derived from key when omitted. |
width / flex |
width |
— |
valueGetter: (value, row) => … |
accessor: (row) => … |
AdaptTable reads the row directly — no v7/v8 signature churn. |
valueFormatter / renderCell |
accessor / Cell |
Cell is a component receiving { row, rowIndex }. |
type: "singleSelect", valueOptions |
filter: { type: "select", options } |
Turns the column into a native select filter. |
sortable |
sortable |
Add sortValue for formatted/JSX cells. |
align / headerAlign |
align |
Use logical "start"/"center"/"end". |
Before / after
Section titled “Before / after”Before — MUI X, reaching for <DataGridPro> to pin and resize columns:
import { DataGridPro } from "@mui/x-data-grid-pro"; // paid tier
function PeopleGrid({ rows }: { rows: Person[] }) { return ( <DataGridPro rows={rows} getRowId={(r) => r.id} checkboxSelection columns={[ { field: "name", headerName: "Name", sortable: true }, { field: "role", headerName: "Role" }, { field: "status", headerName: "Status", type: "singleSelect", valueOptions: ["active", "retired"], }, ]} initialState={{ pinnedColumns: { left: ["name"] } }} /> );}After — @adapttable/mui, all MIT, with a status filter + chip and URL state
for free:
import { DataTable } from "@adapttable/mui";
function PeopleTable({ people }: { people: Person[] }) { return ( <DataTable data={people} rowKey={(r) => r.id} enableColumnMenu // show/hide, reorder, pin — no Pro licence resizableColumns bulkActions={ [ /* … turns on multi-row selection */ ] } columns={[ { key: "name", sortable: true }, { key: "role" }, { key: "status", filter: { type: "select", options: "auto" } }, ]} /> );}Escaping the version churn (v6 → v7 → v8 breaking changes)
Section titled “Escaping the version churn (v6 → v7 → v8 breaking changes)”A big reason teams migrate is that each DataGrid major rewrites your code. If one of these breaking changes brought you here, the right column is what the same thing looks like in AdaptTable — where it hasn’t changed since 1.0:
| DataGrid breaking change | In AdaptTable |
|---|---|
valueGetter signature rewritten in v7: (params) => … became (value, row, column, apiRef) => … (same for valueFormatter) |
accessor: (row) => … — one argument, the row, stable API |
disableSelectionOnClick renamed to disableRowSelectionOnClick (v6) |
selection never binds to row clicks; onRowClick is a separate, explicit prop |
rowSelectionModel reshaped in v8: a plain id array became { type: "include" | "exclude", ids: Set } |
onSelectionChange hands you a plain string[] — no model object |
| Row virtualization moved to Pro in v8 (Community pages cap at 100 rows) | virtualize — free, MIT, no page cap |
| Multi-column sorting / multi-filters gated behind Pro | multiSort and multi-condition filters — free |
| Column resizing gated behind Pro | resizableColumns — free |
One declarative API, semver-stable, and the features that keep moving behind the paywall are simply included.
Gotchas
Section titled “Gotchas”rowKeyis required — the equivalent ofgetRowId, but with noiddefault.- No
sortModel/filterModel/paginationModelobjects. Sorting is a per-columnsortableflag; filtering is declarativefiltershorthands; pagination is automatic. For a server API, emit one query viaonQueryChangeinstead of three*Mode="server"props. valueGetterbecomesaccessor. AdaptTable passes the row, so the v7/v8(value, row, column, apiRef)signature change doesn’t apply — and a JSX cell usesCell(a{ row, rowIndex }component).- Selection is a plain id list. No v8
{ type: "include" | "exclude", ids: Set }model —onSelectionChangehands youstring[]. - Row expansion replaces master-detail.
renderRowDetailis the free equivalent of the ProgetDetailPanelContent. summaryRowis a footer, not aggregation. You compute the totals from the page’s rows; there is no Premium aggregation engine (and none needed for a sum/count footer).
Where next
Section titled “Where next”- Getting started · Data tiers · Filtering · Column management.
- Virtualization — window large lists, free.
- Comparison — where each library fits.