LWC Datatable Advanced
lightning-datatable ships with the patterns that turn it into a
real grid: inline edit with batched save, sortable columns, row
selection, row actions, infinite scroll via onloadmore, custom
cell types via subclassing, and row-level errors that highlight
specific cells. The hard part is composing them together without
fighting the framework.
The recurring shape is: rows come from @wire(getRecords) or an
Apex method, columns are a JS array with editable: true flags,
the user changes cells, the table emits draftValues on Save,
the controller calls updateRecord (one per row, in parallel) and
clears drafts on success / surfaces row-level errors on failure.
Infinite scroll layers on by setting enable-infinite-loading
and handling onloadmore. Custom cell types layer on by importing
LightningDatatable and extending it.
The mistakes are about state management. Engineers persist data
as an array reference and mutate it (LWC reactivity drops);
engineers don't dedupe row IDs across pages (infinite scroll
duplicates rows on hot reload); engineers fire updateRecord
sequentially in a loop and burn 30 seconds on 50 rows when they
could Promise.all the lot.
Recommended Workflow
- Define columns as a class field, not in the template. A
JS array
columns = [{label, fieldName, type, editable, sortable}]
with data and draftValues is the canonical shape. Inline
<lightning-datatable columns={...}> is harder to read.
- Bulkify the inline-edit save. On
onsave, build an array
of {fields: {Id, ...changedFields}} from event.detail.draftValues,
then Promise.all(updateRecord(...)) them. Sequential saves
make 50-row commits feel broken.
- Reset
draftValues only after the save succeeds. Setting
this.draftValues = [] on submit clears the drafts but loses
the user's edits if the save fails mid-flight. Reset in the
then of Promise.all.
- For sorting, decide client-side vs server-side up front.
Client-side: implement
sortBy / sortDirection and mutate a
local copy of data. Server-side: refetch with the new sort,
reset infinite scroll. Mixing the two leads to inconsistent
ordering after pagination.
- For infinite scroll, dedupe by row Id on every append. A
refresh or hot reload can cause the same row to be appended
twice.
data = [...data, ...newRows.filter(n => !existing.has(n.Id))].
- Custom cell types: extend
LightningDatatable. Import
LightningDatatable from lightning/datatable, register the
custom type with a template. The template runs in the table's
shadow DOM, not your component's — class names must come from
SLDS or be set via --slds-c-* tokens.
- Show row-level errors via
errors attribute. Map
{rowId: {messages, fieldNames, title}} after a failed save
so the user sees the exact cell that failed.
What This Skill Does Not Cover
- Fully custom grids (AG Grid, Tabulator) — use those libraries
in their own LWC wrapper; outside this skill's scope.
- Read-only datatables for fewer than ~50 rows — plain
lightning-datatable with no extensions is sufficient.
- Tree grids — see
lwc/lwc-tree-grid.
1---2name: lwc-datatable-advanced3description: Advanced lightning-datatable — inline edit, custom cell types, infinite scroll, row errors. Triggers: datatable inline edit, custom cell type, onloadmore. NOT for first-time datatable setup — use lwc/lwc-data-table. NOT for fully custom virtual grids — use lwc/lwc-virtualized-lists.4---56# LWC Datatable Advanced78`lightning-datatable` ships with the patterns that turn it into a9real grid: inline edit with batched save, sortable columns, row10selection, row actions, infinite scroll via `onloadmore`, custom11cell types via subclassing, and row-level errors that highlight12specific cells. The hard part is composing them together without13fighting the framework.1415The recurring shape is: rows come from `@wire(getRecords)` or an16Apex method, columns are a JS array with `editable: true` flags,17the user changes cells, the table emits `draftValues` on Save,18the controller calls `updateRecord` (one per row, in parallel) and19clears drafts on success / surfaces row-level errors on failure.20Infinite scroll layers on by setting `enable-infinite-loading`21and handling `onloadmore`. Custom cell types layer on by importing22`LightningDatatable` and extending it.2324The mistakes are about state management. Engineers persist `data`25as an array reference and mutate it (LWC reactivity drops);26engineers don't dedupe row IDs across pages (infinite scroll27duplicates rows on hot reload); engineers fire `updateRecord`28sequentially in a loop and burn 30 seconds on 50 rows when they29could `Promise.all` the lot.3031## Recommended Workflow32331. **Define columns as a class field, not in the template.** A34 JS array `columns = [{label, fieldName, type, editable, sortable}]`35 with `data` and `draftValues` is the canonical shape. Inline36 `<lightning-datatable columns={...}>` is harder to read.372. **Bulkify the inline-edit save.** On `onsave`, build an array38 of `{fields: {Id, ...changedFields}}` from `event.detail.draftValues`,39 then `Promise.all(updateRecord(...))` them. Sequential saves40 make 50-row commits feel broken.413. **Reset `draftValues` only after the save succeeds.** Setting42 `this.draftValues = []` on submit clears the drafts but loses43 the user's edits if the save fails mid-flight. Reset in the44 `then` of `Promise.all`.454. **For sorting, decide client-side vs server-side up front.**46 Client-side: implement `sortBy` / `sortDirection` and mutate a47 local copy of `data`. Server-side: refetch with the new sort,48 reset infinite scroll. Mixing the two leads to inconsistent49 ordering after pagination.505. **For infinite scroll, dedupe by row Id on every append.** A51 refresh or hot reload can cause the same row to be appended52 twice. `data = [...data, ...newRows.filter(n => !existing.has(n.Id))]`.536. **Custom cell types: extend `LightningDatatable`.** Import54 `LightningDatatable` from `lightning/datatable`, register the55 custom type with a template. The template runs in the table's56 shadow DOM, not your component's — class names must come from57 SLDS or be set via `--slds-c-*` tokens.587. **Show row-level errors via `errors` attribute.** Map59 `{rowId: {messages, fieldNames, title}}` after a failed save60 so the user sees the exact cell that failed.6162## What This Skill Does Not Cover6364- **Fully custom grids (AG Grid, Tabulator)** — use those libraries65 in their own LWC wrapper; outside this skill's scope.66- **Read-only datatables for fewer than ~50 rows** — plain67 `lightning-datatable` with no extensions is sufficient.68- **Tree grids** — see `lwc/lwc-tree-grid`.