Table Inline Editing
Correct one record in place, with an isolated draft and a clear commit or cancel.
| Record | Item | On hand | Listed | Actions |
|---|---|---|---|---|
| Desk lamp | Desk lamp | 12 | Yes | |
| Reading light | Reading light | 5 | No |
Edit one record at a time. Save commits the row; Cancel discards the draft.
| Record | Service | Seats | Confirmed | Actions |
|---|---|---|---|---|
| Morning slot | consultation | 4 | Yes | |
| Afternoon slot | review | 2 | No |
Edit one record at a time. Save commits the row; Cancel discards the draft.
Customise
Code
const rows = [
{ id: "shelf-a", label: "Desk lamp", values: { name: "Desk lamp", stock: 12, listed: true } },
];
const columns = [
{ key: "name", label: "Item", type: "text" },
{ key: "stock", label: "On hand", type: "number", min: 0, max: 500, step: 1 },
{ key: "listed", label: "Listed", type: "checkbox" },
];
<TableInlineEditing
caption="Inventory corrections"
rows={rows}
columns={columns}
onCommit={handleCommit}
/>Table Inline Editing presents caller-owned records in a semantic table and lets a person edit one row at a time without navigating away. Save sends a copied row draft to the parent's typed callback; Cancel discards that draft. The parent remains responsible for applying or persisting accepted values.
Each row has a stable unique id and a short accessible label. Columns use native text, number, select or checkbox controls for string, finite number and boolean values. Fields and actions include the row name, and focus moves into the editor then returns to that row's Edit action after Save or Cancel.
This component does not add sorting, filtering, paging, selection, bulk editing, asynchronous persistence or a validation framework. Number fields use native bounds and step; invalid drafts stay in edit mode with a clear status.
Install
Add the @te registry to your components.json once:
{
"registries": {
"@te": "https://templateempire.io/r/{name}.json"
}
}Then install the component:
npx shadcn@latest add @te/table-inline-editingUsage
import { useState } from "react";
import { TableInlineEditing } from "@/components/table-inline-editing";
import type { TableInlineEditingChange, TableInlineEditingRow } from "@/components/table-inline-editing";
const initialRows: readonly TableInlineEditingRow[] = [
{ id: "record-1", label: "Desk lamp", values: { name: "Desk lamp", stock: 12, listed: true } },
];
const columns = [
{ key: "name", label: "Item", type: "text" as const, maxLength: 60 },
{ key: "stock", label: "On hand", type: "number" as const, min: 0, max: 500, step: 1 },
{ key: "listed", label: "Listed", type: "checkbox" as const },
];
export function InventoryCorrections() {
const [rows, setRows] = useState(initialRows);
const commit = ({ rowId, values }: TableInlineEditingChange) => {
setRows((current) => current.map((row) => row.id === rowId
? { ...row, label: String(values.name ?? row.label), values: { ...values } }
: row));
};
return <TableInlineEditing caption="Inventory records" rows={rows} columns={columns} onCommit={commit} />;
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| caption | string | required | Visible semantic table caption. |
| rows | readonly TableInlineEditingRow[] | required | Rows with stable unique ids, a short row label and caller-owned primitive values. The parent updates this collection after a commit. |
| columns | readonly TableInlineEditingColumn[] | required | Ordered text, number, select or checkbox editor definitions. |
| onCommit | (change: TableInlineEditingChange) => void | required | Receives the row id and a fresh complete values object after a valid Save. No persistence or async result is implied. |
| className | string | undefined | Additional classes merged onto the table shell. |
Customisation
The table, editors and focus rings use semantic surface, foreground, muted, border, input, primary and ring tokens. Consumers own surrounding form layout and the decision to accept or persist the typed commit.
<TableInlineEditing
caption="Inventory records"
rows={rows}
columns={columns}
onCommit={commit}
className="max-w-5xl"
/>Frequently asked questions
- Does Save mutate my row objects?
- No. The callback receives a new values object, and the component does not mutate the input rows. The parent decides how to replace or persist a record.
- Can two rows be edited at once?
- No. Only one isolated draft is open at a time; other Edit actions are disabled until that draft is saved or canceled.
- Does this include paging, sorting or bulk edits?
- No. It only handles one row's inline draft. Compose it with the table tools your data view needs.