Skip to content

Migrate from MUI X DataGrid

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.

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 rowssummaryRow (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 six UI kits — the same code renders in Mantine, Chakra, Ant Design, Radix, 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.

Terminal window
pnpm add @adapttable/core @adapttable/mui @mui/material

Works with the default theme; wrap in <ThemeProvider> to customize, exactly as DataGrid does.

<DataGrid><DataTable>:

MUI X DataGrid@adapttable/muiNotes
rowsdataFrontend tier. Server tier: data (page) + total + onQueryChange.
columnscolumnsGridColDefColumnDef, mapped below.
getRowIdrowKeyRequired(row) => string.
sortModel / onSortModelChangesortable per column (+ multiSort)AdaptTable owns and applies sort state.
sortingMode: "server"onQueryChange (or source)The consolidated query carries sortBy/sortDir.
filterModel / onFilterModelChangecolumn filter shorthand + table filtersWidgets, chips, and URL params are derived for you.
paginationModel / onPaginationModelChangeautomatic (frontend) or total + onQueryChangeNo 100-row community cap.
checkboxSelection + rowSelectionModelbulkActions, or selectedIds / onSelectionChangeMulti-row selection is free; no { type, ids } model to manage.
getDetailPanelContent (Pro)renderRowDetail(row) => ReactNode.
loadingloading
slots.noRowsOverlay / loadingOverlayslots.empty / slots.skeleton
initialState / apiRef.exportState() (state only)urlSync + urlKey (+ savedViews)State lives in the URL, shareable and reload-safe.
pinnedColumns (Pro) / columnVisibilityModelenableColumnMenu / columnLayoutShow/hide, reorder, pin in one menu.

GridColDefColumnDef:

MUI X DataGrid@adapttable/muiNotes
fieldkeyAlso the value path (dot paths supported).
headerNameheaderAuto-derived from key when omitted.
width / flexwidth
valueGetter: (value, row) => …accessor: (row) => …AdaptTable reads the row directly — no v7/v8 signature churn.
valueFormatter / renderCellaccessor / CellCell is a component receiving { row, rowIndex }.
type: "singleSelect", valueOptionsfilter: { type: "select", options }Turns the column into a native select filter.
sortablesortableAdd sortValue for formatted/JSX cells.
align / headerAlignalignUse logical "start"/"center"/"end".

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" } },
]}
/>
);
}
  • rowKey is required — the equivalent of getRowId, but with no id default.
  • No sortModel/filterModel/paginationModel objects. Sorting is a per-column sortable flag; filtering is declarative filter shorthands; pagination is automatic. For a server API, emit one query via onQueryChange instead of three *Mode="server" props.
  • valueGetter becomes accessor. AdaptTable passes the row, so the v7/v8 (value, row, column, apiRef) signature change doesn’t apply — and a JSX cell uses Cell (a { row, rowIndex } component).
  • Selection is a plain id list. No v8 { type: "include" | "exclude", ids: Set } model — onSelectionChange hands you string[].
  • Row expansion replaces master-detail. renderRowDetail is the free equivalent of the Pro getDetailPanelContent.
  • summaryRow is 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).