Server-side CRUD
Server-side CRUD
With a complete dataProvider configuration, Handsontable sends create, update, and remove operations to your backend. For loading and fetchRows, see Configuration and query parameters and Fetching, hooks, and examples.
Create, update, and remove
With a complete dataProvider configuration, Handsontable sends create, update, and remove operations to your backend through three callbacks. Valid edits appear in the grid immediately; if the server rejects an update (or the mutation promise rejects), or if beforeRowsMutation returns false, affected cells roll back. Cell and column validators run before onRowsUpdate; if any cell in the batch fails, Handsontable does not call onRowsUpdate, fires afterRowsMutationError with a validation failure, and reverts the edit. If rowId resolves to null or undefined for a row, Handsontable cannot send an update or remove for that row (edits revert; remove from the UI throws). Programmatic updateRows and removeRows throw if an id is missing. Row insert from the context menu is skipped when the table already has as many rows as maxRows.
Update lifecycle
When a user edits a cell, the update flows through these steps in order:
- Cell and column validators run on the edited cells. If any cell fails validation, the edit is reverted and
afterRowsMutationErrorfires with a validation failure. The remaining steps do not run. beforeRowsMutationfires with('update', { rows }). Returnfalseto cancel — the optimistic values revert andonRowsUpdateis not called.- Optimistic UI update — the new cell values appear in the grid immediately.
onRowsUpdate— your server callback runs with the batch of changes.- On success:
afterRowsMutationfires, then Handsontable refetches the current page (withskipLoading: trueso the loading overlay does not flash). - On failure: the optimistic values roll back and
afterRowsMutationErrorfires. Ifdialogis enabled, an error modal appears.
onRowsCreate
Called when the user inserts rows (for example from the context menu). Payload shape:
position:'above'or'below'.referenceRowId: anchor row id when inserting next to a row (fromrowId); may beundefinedwhen there is no anchor (for example some programmatic inserts).rowsAmount: how many rows to create in one request.
Your API should create the rows and return a promise. Handsontable refetches the current query after success.
Create, update, and remove requests are serialized: if the user triggers another mutation before the previous one finishes, work runs in order so your backend sees a single stream of operations.
onRowsUpdate
Called with an array of { id, changes, rowData }:
id— stable row id (same asrowId).changes— map of property names to new cell values.rowData— optional full row snapshot; Handsontable fills it when applying edits from the grid.
One batch usually corresponds to one user action (typing a cell, paste, autofill, clear column, and similar). Implement your PATCH or PUT logic here, then rely on the refetch that follows a successful mutation.
onRowsRemove
Called with an array of row ids to delete. After success, Handsontable refetches and may move to the previous page if the current page becomes empty.
Programmatic CRUD
From the plugin instance (hot.getPlugin('dataProvider')), you can also call createRows, updateRows, and removeRows with the same shapes as the callbacks above.
Mutation hooks
beforeRowsMutation—(operation, payload); returnfalseto cancel. For create and remove, the server callback is not invoked and there is no refetch. For update from the grid,falsereverts optimistic cell values and skipsonRowsUpdate; cell validators run only when the hook allows the mutation to continue.afterRowsMutation— runs after the server mutation callback succeeds and before the post-mutation refetch.afterRowsMutationError— runs when the mutation callback throws or rejects, when validation fails before the request, or when the refetch after a successful update fails.
operation is 'create', 'update', or 'remove'. The hook payload is a wrapper object, not the same reference as the callback argument: 'create' uses { rowsCreate } (same inner shape as onRowsCreate), 'update' uses { rows } (the array passed to onRowsUpdate), and 'remove' uses { rowsRemove } (the id array passed to onRowsRemove).
Undo stack
When onRowsUpdate is set, Handsontable skips stacking certain edit sources on the local undo stack so client undo does not fight server-backed data (including edit, paste, cut, autofill, Clear column from the context menu, and revert after a failed onRowsUpdate). See Undo/Redo for the general model.