Row-Level Edit Mode Versus Cell-Level Editing
Permalink to "Row-Level Edit Mode Versus Cell-Level Editing"Editable grids typically offer one of two models. In cell-level editing, users edit one cell at a time: Enter or F2 opens an editor in the cell, Enter commits, Escape cancels. In row-level editing, an “Edit” action turns the whole row into form fields, with Save and Cancel buttons; changes commit together. Both can be accessible, but they suit different data and have different focus, validation and announcement needs. Mixing them inconsistently across a product confuses everyone.
This guide belongs to inline editing & form controls.
Spec reference
Permalink to "Spec reference"- SC 3.3.1, 3.3.3 (A, AA) — errors identified and suggestions given, per field.
- SC 4.1.2 (A) — editors and buttons named; row edit state exposed.
- SC 2.4.3 (A) — focus enters and leaves edit mode predictably.
- SC 3.3.4 (AA) — for consequential data, review before submission (row Save provides a natural point).
- SC 4.1.3 (AA) — save results announced.
When to use — and when not to
Permalink to "When to use — and when not to"Use cell-level editing for independent values — quantities, prices, statuses — and spreadsheet-like workflows. Use row-level editing when fields depend on each other (start and end dates, currency and amount), when changes should be reviewed together, or when edits trigger expensive server validation.
Do not mix both models in one grid without a clear visual and textual indication. The misapplication: a grid where some columns edit in place and others open a row form, with no indication which is which.
Annotated code example
Permalink to "Annotated code example"<!-- Row in edit mode: fields labelled by their column headers -->
<tr data-editing="true" aria-describedby="row-42-errors">
<th scope="row">INV-1042</th>
<td><label class="visually-hidden" for="due-42">Due date</label>
<input id="due-42" type="date" value="2026-10-15" aria-describedby="due-42-err"></td>
<td><label class="visually-hidden" for="amt-42">Amount (EUR)</label>
<input id="amt-42" inputmode="decimal" value="1240.00"></td>
<td>
<button type="button" id="save-42">Save INV-1042</button>
<button type="button" id="cancel-42">Cancel editing INV-1042</button>
</td>
</tr>
<p id="row-42-errors" class="error-summary" hidden></p>
function enterRowEdit(row) {
renderRowEditors(row);
row.querySelector('input, select, textarea').focus(); // first field (SC 2.4.3)
status.textContent = `Editing ${row.dataset.id}. Save or Cancel when done.`;
}
async function saveRow(row) {
const errors = validateRow(row); // per field (SC 3.3.1)
if (errors.length) { showFieldErrors(errors); focusFirstError(errors); return; }
await api.save(row.dataset.id, valuesOf(row));
exitRowEdit(row);
row.querySelector('th').focus(); // back to the row header
status.textContent = `${row.dataset.id} saved.`; // SC 4.1.3
}
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Model | Key | Expected | Announcement |
|---|---|---|---|
| Cell | Enter / F2 | Edit cell | “edit, 1,240.00” |
| Cell | Enter | Commit | Cell value |
| Cell | Escape | Cancel | Original value |
| Row | Enter on Edit | Fields appear; focus first field | “Editing INV-1042…” |
| Row | Tab | Next field, then Save, Cancel | Field labels |
| Row | Save with errors | Focus first error | Error text |
| Row | Save | Exit; focus row header | “INV-1042 saved.” |
| Row | Escape or Cancel | Discard; focus row header | “Changes discarded.” |
Integration context
Permalink to "Integration context"Cell-level details are in entering and exiting cell edit mode and inline form validation inside editable table cells. Undo for both models is covered in undo and cancel affordances for inline cell edits. Spreadsheet-style entry, the extreme of cell-level editing, is in Enter and Tab data entry in spreadsheet grids.
Gotchas
Permalink to "Gotchas"Unsaved changes. Navigating away from a row in edit mode should prompt or auto-save; never discard silently.
Grid keyboard model in row edit. In a role="grid", row edit mode must suspend arrow-key navigation so arrows work inside inputs.
Labels. Inputs inside cells need labels — visually hidden labels or aria-labelledby to the column header and row header.
Worked example
Permalink to "Worked example"An HR grid mixed cell-level editing for status and row-level editing for contract dates without indication, and contract date errors appeared only as red borders. Screen reader users could not tell which model applied or why saves failed. The team switched the whole grid to row-level editing (fields depend on each other), labelled inputs with column names, named Save and Cancel with the employee, summarised errors with links to fields, and announced saves. Cell-level editing remained in a separate quick-status grid where it fit better.
Common mistakes
Permalink to "Common mistakes"- Mixed models without indication.
- Unlabelled inputs in edit rows.
- Errors shown only by colour.
- Focus left on removed Save buttons.
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Offer editMode: 'cell' | 'row' at grid level with built-in focus handling, labels from column definitions and messages. Discourage mixing within one grid.
Step-by-step implementation
Permalink to "Step-by-step implementation"- Pick one model per grid. Row-level edit mode suits forms with validation across fields; cell-level editing suits spreadsheet-like data entry. Mixing both in one grid confuses users about when a change is saved.
- Row-level: enter and exit explicitly. An “Edit” button turns the row’s cells into labelled inputs and shows Save and Cancel. Focus moves to the first input; Save returns focus to the Edit button and announces “Row saved”.
- Cell-level: Enter or F2 to edit. In a grid, Enter or F2 opens the editor in place; Enter commits and moves down, Escape cancels, Tab commits and moves right.
- Label every editor. Inputs inside cells need names that include row and column context, such as “Amount for INV-1042”, because the visible column header is not automatically the input’s label.
- Surface validation where the user is. Errors appear next to the field with
aria-describedbyandaria-invalid; in row mode, Save is blocked and focus moves to the first invalid field.
Edge cases
Permalink to "Edge cases"- Navigating away mid-edit. Decide whether leaving a row commits or discards; then tell users, and never discard silently.
- Concurrent edits. If another user saved the row, show the conflict and offer to keep yours or theirs.
- Read-only cells. In row mode, read-only cells stay as text with
aria-readonlyon the cell, not as disabled inputs that clutter the tab order.
FAQ
Permalink to "FAQ"Which model is more accessible?
Both can be. Row-level uses familiar form behaviour; cell-level is faster for spreadsheets. Choose by data.
Should Enter save a row in row edit mode?
Enter in a single-line input can submit the row if documented; many teams require the Save button to avoid accidental saves.
How should errors be presented in row mode?
Inline per field and a short summary linked to fields, with focus to the first error.
Can row edit open in a dialog instead?
For long forms, yes; then dialog focus rules apply.
How do I indicate a row is in edit mode?
Visually (border, background plus text) and in the row’s description or a status message.
What about bulk editing several rows?
Use a bulk edit dialog with fields that apply to selected rows, and summarise results.
Related
Permalink to "Related"- Entering and exiting cell edit mode — cell model
- Inline form validation — errors
- Error summaries for batch failures — summarising errors