Editable Table

Records the user corrects in place, like a small spreadsheet. The agent gets back only what changed.

Pattern · Shares patterns with Data Table, Change Preview

≈ 1,450 tokens per use (tool 822 · guidance 301 · typical payload 327)How token costs are estimated

Example: Editable Table

Let me clean up our design tool list before renewals.

Design stack

Arrow keys move between cells. Enter or typing edits a cell, Escape cancels an edit or, on a selected cell, clears the selection, Delete clears the value.

FigmaDesignfigma.comMar 12, 2026
LinearIssue trackinglinear.appJan 4, 2026
VercelHostingvercel.comAug 21, 2026
FramerWebsiteframer.comMay 2, 2026
LoomVideoloom.comNov 18, 2026

Click a cell twice to edit

When to use

  • The user wants to fix, fill in or update several records with the same fields: "let me clean up our tool list", "update the renewal dates".
  • The user will add or remove a few rows and change values across them before anything is saved.

When not to use

  • The user wants to read, sort or act on records in bulk without changing values. Use a Data Table.
  • You're proposing specific changes for the user to approve. Use a Change Preview with before and after values.
  • One record with many fields. Use a Generated Form.
  • Editing generated text. Use the Inline Editor.

How the agent should use it

  • Every row comes from a tool result. Never invent records or fill blanks with guesses; leave a missing value null.
  • Keep to the 2–6 columns the user will edit, with the identifying column first.
  • Give a select column every allowed value as options, so the user can't type one the system rejects.
  • Mark computed or system fields readOnly.
  • The result is intent, not a write. Apply the changes with your tools, then report what was saved.

Interaction

  • Click a cell to select it; its address (A1, B3) shows in the footer. Click it again, double-click, press Enter or just start typing to edit.
  • Enter saves and moves down, Tab moves right, Escape cancels an edit. Escape on a selected cell, like a click outside the cells, lets it go. Delete or Backspace clears the selected cell's value.
  • Clicking anywhere outside the cells (the page, the title, the footer) lets the selected cell go, saving an open edit first. A value the editor rejects keeps its cell for one click so the reason shows; the next click elsewhere drops it and the cell keeps its old value.
  • select and date cells show a chevron once selected, since that click opens their options or the date picker. Clicking the cell while it's open closes it.
  • Column headers sort the table: ascending, descending, then the agent's order.
  • Row checkboxes select rows to delete. Add row appends an empty row and starts editing it.
  • Undo and redo cover every edit, addition and deletion, from the header buttons or Ctrl/⌘ Z and Shift Ctrl/⌘ Z.
  • Save changes is enabled once something changed. After saving, the table locks and says what was saved.

Design

  • Each header names its type with a 14px icon (text, number, select, link, date) so the user knows what a cell accepts before editing it.
  • select values are tags coloured by their place in options: the first five take the series colours (tinted fill, text in the same hue), the rest stay blue. An option keeps its colour however the rows change, and the label always shows, so colour is never the only key.
  • A row's logo shows as a 20px mark beside the first column, with a dark file for marks that would vanish on a dark card. Links show the domain, underlined.
  • The selected cell and the open editor carry the violet selection ring; a rejected value turns the ring red and the reason replaces the cell address in the footer.
  • The selection and first columns stay put while the table scrolls sideways on narrow screens.

Accessibility

  • The table is an ARIA grid with one tab stop: arrow keys move between cells, Left and Right wrapping to the previous or next row (Tab and Shift+Tab do the same while editing). Home and End go to the row's ends; pressed again there, they move to the previous or next row's first or last cell. With Ctrl or ⌘, they go to the table's corners. Page Up and Page Down move five rows up or down in the same column, stopping at the first and last rows.
  • Every editor is named after its column and row ("Category, Figma"). Edits, deletions, undo and validation errors are announced in a polite live region.
  • Empty cells read as "Empty"; read-only cells are marked aria-readonly; select and date cells say they open a list or a dialog (aria-haspopup).

Installation

npx shadcn@latest add https://your-site.com/r/editable-table.json

Usage

app/api/chat/route.ts
import { tool } from "ai"
import { editableTableTool } from "@/components/genui/editable-table.schema"

const tools = {
  // No execute(): the call is completed when the user saves.
  [editableTableTool.name]: tool({
    description: editableTableTool.description,
    inputSchema: editableTableTool.inputSchema,
  }),
}
components/message.tsx
import { EditableTable } from "@/components/genui/editable-table"

function ToolPart({ part, addToolResult }) {
  if (part.type !== "tool-edit_table") return null

  return (
    <EditableTable
      {...part.input}
      status={part.state === "input-streaming" ? "streaming" : undefined}
      result={part.state === "output-available" ? part.output : undefined}
      onResult={(result) =>
        addToolResult({
          tool: "edit_table",
          toolCallId: part.toolCallId,
          output: result,
        })
      }
    />
  )
}

The agent receives only what changed: the edited cells of each existing row, the cells of new rows, and the ids of deleted rows.

{
  action: "save",
  updated: [{ id: "tool_loom", cells: { category: "Video", renews: "2026-12-01" } }],
  added: [{ cells: { tool: "Notion", category: "Design" } }],
  deleted: ["tool_framer"],
}

Restoring from history

Pass the stored result back as result. The component replays the saved edits onto the original rows, renders the table locked and never calls onResult again.

Schema

PropTypeNotes
titlestringWhat the records are, e.g. "Design stack"
descriptionstring?Scope, e.g. the event date
columnsColumn[]1–6 columns
rows{ id, cells, logo? }[]Data-bound. Up to 100. cells maps column keys to a string, number or null. logo is { url, darkUrl?, alt? }, only when the data has one
allowAddbooleanDefault true
allowDeletebooleanDefault true. Off hides the row checkboxes
sort{ key, direction }?Initial sort
submitLabelstring?Save button label. Default "Save changes"

Column

FieldTypeNotes
keystringMatches keys in cells
labelstringHeader text, 32 characters at most
typetext | number | select | url | dateDefault text. date takes YYYY-MM-DD
optionsstring[]?For select: up to 12 allowed values. The first five get their own tag colours
readOnlyboolean?The user can't change this column
sortablebooleanDefault true

Host props: status (streaming, ready or submitted), onResult, result, surface and className.

Source

On this page