Home / lwc / lwc-datatable-advanced
lwc-datatable-advanced
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.
Open the skill folder on GitHub
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}]withdataanddraftValuesis the canonical shape. Inline<lightning-datatable columns={...}>is harder to read. - Bulkify the inline-edit save. On
onsave, build an arrayof
{fields: {Id, ...changedFields}}fromevent.detail.draftValues, thenPromise.all(updateRecord(...))them. Sequential saves make 50-row commits feel broken. - Reset
draftValuesonly after the save succeeds. Settingthis.draftValues = []on submit clears the drafts but loses the user's edits if the save fails mid-flight. Reset in thethenofPromise.all. - For sorting, decide client-side vs server-side up front.
Client-side: implement
sortBy/sortDirectionand mutate a local copy ofdata. 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. ImportLightningDatatablefromlightning/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
errorsattribute. 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-datatablewith no extensions is sufficient. - Tree grids — see
lwc/lwc-tree-grid.