# Pricing Plan Editor With Per-Row Edit Mode and Inline Validation

> Per-row edit-mode data grid for pricing plans: the table is read-only until one row is checked out with Edit, its fields turn into inline inputs and selects bound to a single draft, and Save or Cancel commits or reverts that one row on its own with no global save bar and no bulk actions

A paid shadcn/ui block sold by the [@reui](https://reui.io/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/reui/data-grid-editing-4).

- Page, with a preview: https://shadcn.exchange/r/reui/data-grid-editing-4
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/reui/data-grid-editing-4-light-bcfca47d262cd29f.png
- Listed: 2026-10-13

## Buy and install

@reui sells this block, and its code is not on shadcn.exchange. Buy it from the registry, which installs it for those who have: https://shadcn.exchange/out/reui/data-grid-editing-4

## Dependencies

- npm packages: `@tanstack/react-table`, `cn`, `sonner`
- Registry items: `@reui/badge`, `@reui/data-grid`, `@reui/frame`, `alert-dialog`, `button`, `@reui/data-grid-column-header`, `@reui/data-grid-scroll-area`, `@reui/data-grid-table`, `dropdown-menu`, `input`, `select`, `switch`, `tooltip`

## Notes from the registry

## Purpose
A per-row edit-mode data grid for billing, revenue, and product teams that maintain a small pricing catalog: the whole table stays read-only until you check out a single plan with Edit, that row's fields become inline editors bound to one draft, and Save or Cancel commits or reverts that row on its own. There is no always-live cell editing, no global save bar, and no bulk selection. It is the row-scoped, one-at-a-time counterpart to a spreadsheet-style grid.

## Best Fit
- pricing and packaging plan editors
- rate cards, tier tables, and subscription plan management
- any admin grid where a row is a record that should be edited and committed as a unit
- catalogs where users expect an explicit Edit, then Save or Cancel, per row

## Main Pieces
- a ReUI `Frame` shell (`FrameHeader`, `FramePanel`, `FrameFooter`) whose muted backdrop frames the grid as an inset panel, with a title zone above and a summary footer below
- header controls: a Visibility filter and an Add plan action, both locked while a row is being edited
- a ReUI `DataGrid` with one row per plan; each row shows read-only values until it is checked out
- per-row edit mode: Edit turns that row into inline editors (text for name, numeric for price, seats, and trial, `Select` for audience and visibility, a live `Switch` for featured), and the action cell swaps to Save and Cancel
- a left accent bar marks the checked-out row, and every other row's Edit is disabled so only one plan edits at a time
- inline validation across the whole row: Save is blocked while any field is invalid, and each message renders under its input in a reserved slot
- a footer that summarizes plan count and public count, or names the row being edited while locked
- row actions for duplicate and delete, both opening the copy in edit mode or confirming with an `AlertDialog`

## Editing Model
- The whole grid is read-only by default. `startEdit` snapshots one plan into a single string-based `draft`; only that row renders editors, and `editingRowId` plus `isLocked` keep every other row and the toolbar disabled until the draft resolves.
- Editing handlers and the draft are threaded into cells through TanStack `table.options.meta`, so the column definitions never rebuild and the checked-out row's inputs stay controlled by the one draft.
- Numeric fields are held as raw input text in the draft so an in-progress value is never lost to an early parse; `draftToPlan` converts them back to numbers only on a validated Save.
- Save commits that row immediately into `rows` and clears the draft; Cancel discards the draft and, for a row added or duplicated in this session, removes it. Each row is its own commit; there is no working-versus-saved diff and no batch save.

## shadcn Primitives Used
- Button
- Input
- Select
- Switch
- DropdownMenu
- AlertDialog
- Tooltip

## ReUI Primitives Used
- Frame
- DataGrid
- DataGridContainer
- DataGridScrollArea
- DataGridTable
- DataGridColumnHeader
- Badge

## Third-Party Libraries
- `@tanstack/react-table` for the core row model, stable row ids, and the cell meta channel
- `sonner` for preview-safe save feedback

## How To Use
1. Replace `PLANS`, `PLAN_AUDIENCES`, and `PLAN_VISIBILITIES` with your real catalog and option sets.
2. Keep raw `price`, `seats`, and `trialDays` as numbers in the data; format only in the cells.
3. Reuse the validators (`validatePlanName`, `validatePrice`, `validateSeats`, `validateTrialDays`) or extend them per field; Save stays disabled while any returns a message.
4. Wire the `saveEdit` commit to your billing API and keep local state as the source of truth for read rows.
5. Keep the editing handlers and the draft on `table.options.meta` and read them with the `PlanTableMeta` type inside cells.

## What Not To Do
- Do not make cells always editable or add a global save bar; the point of this block is one row checked out at a time and committed on its own.
- Do not move editing state into the column factory closure; it forces a column rebuild on every keystroke. Use the meta channel.
- Do not let a second row enter edit mode while one is open; the lock keeps a single draft honest.
- Do not commit an invalid row; the validators must gate Save and the messages must render.
- Do not store the numeric drafts as numbers while typing; keep them as text and parse on Save.
- Do not copy real plan prices, seat caps, or product names into this block.

## Real Data Integration
Back each row with a stable id, name, code, audience, price, seats, trialDays, visibility, and featured flag. For server-backed editing, send one update per Save carrying just that row, reconcile the returned record into local state, and surface server validation errors through the same inline slots. Because each row commits independently, optimistic updates and per-row retries are natural.

## LLM Guidance
Use this block when the user asks for a row-edit or edit-in-place data grid, a pricing plan or tier editor, a rate card, or any table where a record is edited and saved as a unit rather than cell by cell. Preserve the per-row edit mode, the single draft on the meta channel, the one-row lock, the gating validators, the ReUI Frame shell, and the summary footer unless the brief changes the record shape or the editing model. This is deliberately distinct from the always-live-cell, global-save-bar editing grids in this family.
