This guide covers changes to the beta DataTable component. These APIs may continue to evolve before the stable release.
Overview
This guide continues the work started in Column Types and EditConfig, which introduced theeditConfig property. Custom edit mode is a peer to the existing text, number, boolean, select, and multiselect edit experiences, but it is designed for cells whose committed value is an object rather than a scalar.
The DataTable now supports a dedicated custom edit mode for object-backed cells:
editConfig.mode: "custom"— Opens a multi-field editor surface for the cellrenderEditor— Renders your custom editor UI with row context and a typedcontrolleronCommit— Receives the final committed object value when the editor closes normallyonDraftUpdate— Optional advanced callback for consumers that need every local draft mutationvalidateDraft/blockOnValidationError/onRequestClose/surface— Optional hooks for validation, close policy, and surface presentation
Read mode vs edit mode
Custom mode intentionally separates the read and edit paths:renderCellremains the read-mode renderer for the cell.getCellTextis the preferred optional plain-text extractor for sorting when your read view is JSX or multiline.getReadRenderResultis the advanced optional read hook when you want one hook to return both rendered read content and a sortable read string together.renderEditoris the edit-mode renderer for the anchored editor surface.
getCellText by default when you already have a separate renderCell and just need a plain sortable string. Reach for getReadRenderResult only when you need one hook to produce both the rendered read content and the raw sortable string together.
Editor context and controller API
renderEditor receives a context object with:
row— the full row datarowId— the row identifier as a stringvalue— the committed canonical value for the cellcontroller— typed draft, validation, focus, and lifecycle control for the editor
draftValue/initialValueisDirty/changedFieldsvalidationsetDraftValuesetDraftFieldhandleSubmitsubmitdiscardrequestClose(reason)setInitialFocus(focus)
<form onSubmit={controller.handleSubmit}>. The DataTable routes Enter through the nearest enclosing form and calls requestSubmit() on that form. If your custom editor does not render a form, Enter does not implicitly submit the draft. Textareas and other controls that need Enter for their own interaction should continue to manage that key normally.
Close requests and validation
Custom close behavior is explicit:requestClose("submit"), outside click, close button, and programmatic submit commit the current draft by default.requestClose("escape")and pressingEscapediscard the draft by default.- Pressing
Enterinside a custom editor submits only when focus is inside a form wired withonSubmit={controller.handleSubmit}. onRequestClose(request)can returnfalseto keep the editor open and block the default behavior.
validateDraft can return formError and fieldErrors, but those values do not block closing unless blockOnValidationError is true or onRequestClose intercepts the request.
Surface presentation options now live under editConfig.surface.
Example: custom address editor
This example keeps the address as an object, renders a multiline read view, previews validation inside the editor, and commits updates at the row level when the surface closes. Because the editor is wrapped in<form onSubmit={controller.handleSubmit}>, pressing Enter inside a single-line field submits through that form.
Advanced: onDraftUpdate
onDraftUpdate is optional. Use it when you need draft-side telemetry, live preview, or external validation state on every local edit. It is not required for the normal custom editing flow.
Focus management with setInitialFocus
Use setInitialFocus to let the table focus one preferred field when the custom editor opens:
null clears it.