CE01 Simple Prompt Editor
The minimum viable custom editor: open a browser prompt, then commit or cancel
Live Demo
Click a Notes cell, or focus it and press F2 / Space / start typing.
Code
Description
The contract
- Set
editor: 'custom'on the column. Without it,cellEditCallbacknever fires. - The callback receives a single context object — not positional arguments.
- You must call exactly one of
ctx.commit(value)orctx.cancel(), otherwise the grid stays in edit state.
Triggers
The grid handles all the standard triggers (F2, Space, Enter, double-click, click depending on editTrigger, printable char in navigate mode). Your callback only needs to render UI.
CE02 Product Search Dialog
Modal lookup with keyboard nav, highlighted matches, and derived sibling columns
Live Demo
The Product cell stores only a productId; SKU / Category / Unit Price / Total all derive their display from the catalog.
Edit Product on row 3 (empty) to see the dialog. Type "mon", arrow down, Enter — every dependent cell refreshes in the same tick.
Code
Description
Single source of truth
The row stores only productId. SKU, Category, Unit Price and Total all use formatCallback to look up the catalog at render time.
The "stale derived cells" gotcha
By default the grid re-renders only the edited cell. Sibling columns whose formatCallback reads row.productId will show stale values until something else triggers a render. Fix: reassign grid.items = [...rows] in onrowchange.
Alternative — stamp the row
Instead of deriving sibling cells, you could write row.sku, row.category, row.price directly in the dialog before commit. Each column then reads its own field; no full re-render needed.
Keyboard handling
Listen with capture: true and call stopPropagation() on every key the dialog handles. Otherwise the grid's keyboard handler can run on the same event during the focus-return after commit.
CE03 Inline Color Picker Popover
A floating panel anchored to the cell — not every custom editor needs to be a modal
Live Demo
Click a Color cell. Pick a swatch or use the native color input. Click outside or press Esc to cancel.
Code
Description
The editor UI is up to you
A custom editor is just "render whatever you want, then call commit/cancel." Modal dialogs, floating popovers, native pickers, full sidebars — all valid.
Anchoring to the cell
The grid lives in shadow DOM, but cells expose data-row and data-field on each <td>. Query gridElement.shadowRoot (not document) to get the cell and getBoundingClientRect() to position the popover.
Cleanup matters
Always remove the popover element and any global listeners (keydown, mousedown) on commit/cancel — otherwise they leak across edits.
HTML cell content
Use templateCallback when you need raw HTML in the cell — the string it returns is not escaped. formatCallback is for plain text and escapes its return value. Either way, always escape any user input you interpolate.
templateCallback + drafts
templateCallback receives the original row, not the draft. After a custom-editor commits, the new value lives in the draft until you fold it back into items. If you want the cell to reflect the new value immediately, reassign grid.items in onrowchange (same pattern as CE02). formatCallback does not have this quirk — it reads drafts automatically.
CustomEditorContext reference
| Property | Type | Description |
|---|---|---|
value | unknown | The current cell value. |
row | T | The full row object. |
rowIndex | number | Index of the row being edited. |
field | string | The column field name. |
commit(newValue) | (value: unknown) => void | Save the new value and exit edit mode. |
cancel() | () => void | Discard changes and exit edit mode. |
Pre-v1.0.0 betas used a positional callback signature (row, rowIndex, field, cellElement, commit, cancel). That signature no longer fires — always use the single-context form above.