React table inline cell editing — opt-in onCellEdit
▶ Try it live: open a Mantine starter in StackBlitz — this page’s feature is already wired in src/App.tsx (editable columns + onCellEdit); edit it in the browser, no install. Other UI kits →
▶ See it working: edit cells in the live demo — a real table you can type into, not a recording.
Edit a cell in place by passing onCellEdit and marking columns editable.
Omit onCellEdit and the table never opens an editor — even if columns
declare editable. The table never mutates rows; your handler applies the
change.
Example
Section titled “Example”import { useState } from "react";import { DataTable } from "@adapttable/mantine"; // or mui, chakra, antd, radix, base-ui, shadcn, unstyled
interface Person { id: string; name: string; role: string; status: "Active" | "Planned" | "Blocked";}
const SEED: Person[] = [ { id: "1", name: "Aisha", role: "Engineer", status: "Active" }, { id: "2", name: "Jonas", role: "Designer", status: "Planned" },];
export function People() { const [rows, setRows] = useState(SEED); return ( <DataTable data={rows} columns={[ { key: "name", sortable: true, editable: true }, { key: "status", editable: true, editor: { type: "select", options: ["Active", "Planned", "Blocked"], }, }, { key: "role", editable: (row) => row.status !== "Blocked" }, ]} rowKey={(r) => r.id} onCellEdit={(row, key, nextValue) => { setRows((prev) => prev.map((r) => r.id === row.id ? { ...r, [key]: nextValue as never } : r ) ); }} /> );}How it works
Section titled “How it works”- Opt-in.
onCellEditis the switch. Without it, cells stay plain display (package DNA: nothing is pushed on the developer). - Per-column.
editableistrue,false, or(row) => boolean. The editor defaults to"text"; use"number"or{ type: "select", options }for the other kits. - Keyboard. Double-click / Enter / F2 begins; Enter commits; Escape cancels and restores focus; Tab / Shift+Tab commits and advances to the next editable cell.
- One-way data flow. The commit payload is
onCellEdit(row, key, nextValue)— adapters render kit-native inputs; core owns the state machine so every kit behaves the same. - Out of scope (by design): row-level edit mode, validation UI, and optimistic-update helpers — persistence stays with the host.
Options
Section titled “Options”| Prop / field | Type | Default | Description |
|---|---|---|---|
onCellEdit | (row: TRow, key: string, nextValue: unknown) => void | — | Change channel; its presence enables editing. |
editable | boolean | ((row: TRow) => boolean) | — | Whether this column can open an editor (still requires onCellEdit). |
editor | "text" | "number" | { type: "select"; options } | "text" | Widget for the active cell. |
editValue | (row: TRow) => string | — | Draft seed when the displayed cell is formatted but editing needs the raw. |
labels | TableLabels | English | Override editCell for the activate control’s accessible name. |
- Works on desktop rows and mobile cards, LTR and RTL.
- Custom
Cell/accessorstill render in display mode; the editor replaces them only while that cell is active. - Prefer updating your row list immutably in
onCellEditso React sees a newdata/ source identity.
See it live in the demo — double-click the Email column.