@revolist/dash-datagrid
v4.23.24
Published
RevoGrid Core wrapper for Plotly Dash.
Maintainers
Readme
Dash DataGrid
dash-datagrid is the official RevoGrid Core component for
Plotly Dash. It combines RevoGrid's virtualized
custom element with a generated, typed Python class.
- PyPI package:
dash-datagrid - Python import:
dash_datagrid - Python component:
dash_datagrid.RevoGrid - npm package:
@revolist/dash-datagrid - Full documentation: Dash Data Grid
- Core API: RevoGrid API
- Events: RevoGrid Events API
The Python package includes the JavaScript bundle and source map. Normal Dash applications do not need to install the npm package separately.
Compatibility and scope
- Python 3.10 or newer
- Dash 3.x or 4.x
- RevoGrid Core at the same version as
dash-datagrid
Version 1 supports the JSON-safe RevoGrid Core property surface and server-side Dash callbacks. Pro and Enterprise plugin activation, custom JavaScript renderers/editors, imperative grid methods, and synchronous browser-event cancellation are outside its scope.
Getting started
Install
python -m venv .venv
source .venv/bin/activate
python -m pip install dash-datagridOn Windows PowerShell:
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install dash-datagridCreate a grid
Save this as app.py:
from dash import Dash, Input, Output, callback, html
from dash_datagrid import RevoGrid
app = Dash(__name__)
app.layout = html.Main(
[
html.H1("Orders"),
RevoGrid(
id="orders-grid",
columns=[
{
"prop": "order",
"name": "Order",
"readonly": True,
"size": 130,
},
{
"prop": "customer",
"name": "Customer",
"sortable": True,
"size": 180,
},
{
"prop": "amount",
"name": "Amount",
"sortable": True,
"filter": "number",
},
],
source=[
{"order": "A-100", "customer": "Ada", "amount": 120},
{"order": "A-101", "customer": "Grace", "amount": 85},
{"order": "A-102", "customer": "Linus", "amount": 210},
],
rowHeaders=True,
resize=True,
range=True,
filter=True,
style={"height": 420},
),
html.Pre("Edit a cell to see its event.", id="edit-output"),
]
)
@callback(
Output("edit-output", "children"),
Input("orders-grid", "afteredit"),
prevent_initial_call=True,
)
def show_edit(event):
detail = event["detail"]
if "prop" not in detail:
return f"Range edit: {detail}"
return (
f'Row {detail["rowIndex"]}, {detail["prop"]} '
f'changed to {detail["val"]!r}'
)
if __name__ == "__main__":
app.run(debug=True)Run it:
python app.pyThen open the local URL printed by Dash, normally
http://127.0.0.1:8050.
Each column prop selects a key from every source row. Give the component an
explicit height through style; RevoGrid fills that host height.
Loading data
source, pinnedTopSource, and pinnedBottomSource accept lists of
JSON-serializable row dictionaries:
rows = [
{"id": 1, "product": "Keyboard", "price": 95.0},
{"id": 2, "product": "Mouse", "price": 35.5},
]Use strings, finite numbers, booleans, None, lists, and dictionaries made
from those values. Normalize datetime, Decimal, NumPy scalar, UUID, and
other Python-specific values before assigning them to a component prop.
See Data & Rows and Data Source Loading and Syncing.
pandas DataFrames
For a DataFrame that already contains JSON-native values:
rows = frame.to_dict("records")
grid = RevoGrid(
columns=columns,
source=rows,
style={"height": 420},
)For timestamps, missing values, NumPy values, or mixed DataFrames, normalize through JSON:
import json
rows = json.loads(
frame.to_json(
orient="records",
date_format="iso",
)
)The JSON round trip converts timestamps to ISO strings and missing values to
None-compatible JSON null.
Python-driven source changes
Return a new source when the application intentionally loads a different
dataset:
from dash import Input, Output, callback
@callback(
Output("orders-grid", "source"),
Input("reload-orders", "n_clicks"),
prevent_initial_call=True,
)
def reload_orders(_clicks):
return load_orders_from_database()Full replacement is appropriate for a reload, search, page change, or server
refresh. Normal cell edits should usually use the compact afteredit delta
instead.
Configuring columns
Columns are plain dictionaries. The most common JSON-safe fields are:
| Field | Purpose |
| --- | --- |
| prop | Required row key |
| name | Header label |
| size, minSize, maxSize | Width constraints in pixels |
| readonly | Disable editing for this column |
| sortable, order | Enable sorting and set asc or desc order |
| filter | Enable a built-in filter family |
| pin | Use colPinStart or colPinEnd |
| rowDrag | Add a row-drag handle |
| columnType | Use a preset from columnTypes |
columns = [
{
"prop": "id",
"name": "ID",
"readonly": True,
"pin": "colPinStart",
"size": 90,
},
{
"prop": "customer",
"name": "Customer",
"sortable": True,
"minSize": 140,
},
{
"prop": "amount",
"name": "Amount",
"sortable": True,
"filter": "number",
"order": "desc",
},
]Column groups also use plain objects:
columns = [
{
"name": "Customer",
"children": [
{"prop": "first_name", "name": "First name"},
{"prop": "last_name", "name": "Last name"},
],
},
{
"name": "Order",
"children": [
{"prop": "status", "name": "Status"},
{"prop": "amount", "name": "Amount", "filter": "number"},
],
},
]Function-valued column members are not supported. This includes
cellTemplate, columnTemplate, cellProperties, columnProperties,
cellCompare, cellParser, constructor-based editor values, function-based
readonly, and function-based rowDrag.
References:
Reusable column types
columnTypes accepts JSON-safe column presets:
grid = RevoGrid(
columnTypes={
"identifier": {"readonly": True, "size": 120},
"money": {"size": 140, "sortable": True, "filter": "number"},
},
columns=[
{"prop": "id", "name": "ID", "columnType": "identifier"},
{"prop": "amount", "name": "Amount", "columnType": "money"},
],
source=rows,
style={"height": 420},
)Common configuration
Editing, range selection, and clipboard
grid = RevoGrid(
source=rows,
columns=columns,
readonly=False,
range=True,
useClipboard={"rangeFill": True},
applyOnClose=True,
style={"height": 420},
)readonly=Truemakes the complete grid read-only.- A column-level
readonly=Truelocks only that column. range=Trueenables multi-cell selection.useClipboard=Trueenables the built-in copy/paste behavior.rangeFillrequires range selection.
References:
Filtering and sorting
grid = RevoGrid(
filter={
"collection": {
"status": {"type": "eq", "value": "Open"},
}
},
sorting={
"columns": [
{"prop": "amount", "order": "desc"},
],
"additive": False,
},
columns=[
{"prop": "status", "name": "Status", "filter": "string"},
{
"prop": "amount",
"name": "Amount",
"filter": "number",
"sortable": True,
},
],
source=rows,
style={"height": 420},
)Custom filter functions and custom sorting comparators require JavaScript and are not supported through Python in v1.
References:
Row grouping
grid = RevoGrid(
grouping={
"props": ["region", "team"],
"expandedAll": True,
"preserveGroupingOnUpdate": True,
},
columns=columns,
source=rows,
style={"height": 420},
)groupLabelTemplate and getGroupValue are functions and cannot cross the
Python boundary.
References:
Sizing and pinned data
grid = RevoGrid(
colSize=120,
rowSize=34,
rowHeaders={"size": 56},
resize=True,
autoSizeColumn=True,
stretch="last",
canMoveColumns=True,
pinnedTopSource=[{"name": "Current selection"}],
pinnedBottomSource=[{"name": "Total"}],
columns=columns,
source=rows,
style={"height": 500},
)References:
Dash callbacks and RevoGrid events
Dedicated event properties
Seven common event paths have dedicated Dash input properties:
| Dash property | When it updates | Typical detail | Core API |
| --- | --- | --- | --- |
| afteredit | A cell or selected range changes | Compact delta or range data | AfterEditEvent |
| afterfocus | Focus rendering completes | Focused cell/range | FocusAfterRenderEvent |
| headerclick | A header is clicked | JSON-safe column definition | ColumnRegular |
| roworderchanged | A row reorder is requested | from, to | Row Drag and Drop |
| aftersortingapply | Sorting finishes | Final sorting state | AfterSortingApplyEvent |
| beforefilterapply | Filtering is about to apply | Filter collection | Filtering Events |
| aftercolumnresize | Column resizing finishes | Resized columns by index | Column Resize |
Every dedicated and generic callback value has this envelope:
{
"name": "afteredit",
"detail": {
"rowIndex": 0,
"colIndex": 2,
"prop": "amount",
"val": 140,
"type": "rgRow",
"colType": "rgCol"
},
"timestamp": 1784980000000,
"sequence": 1
}nameis the lowercase RevoGrid DOM event name.detailis the JSON-safe event payload.timestampis the browser time in Unix milliseconds.sequenceincrements for every event, so identical consecutive payloads still update Dash.
For a single-cell edit, afteredit.detail keeps rowIndex, colIndex, prop,
val or value, type, and colType. For a range edit, it keeps data,
newRange, oldRange, and type. Large grid-owned models and collections are
removed.
from dash import Input, Output, callback
@callback(
Output("edit-summary", "children"),
Input("orders-grid", "afteredit"),
prevent_initial_call=True,
)
def show_edit(event):
detail = event["detail"]
if "prop" in detail:
return f'{detail["prop"]} changed to {detail["val"]!r}'
return f"Range edit: {detail}"When a callback has several event inputs, use dash.ctx.triggered_id or
dash.ctx.triggered_prop_ids to identify the triggering property.
Subscribe to other events
Set eventListeners to Core event names and read eventData:
grid = RevoGrid(
id="orders-grid",
eventListeners=[
"aftergridinit",
"filterconfigchanged",
"sortingconfigchanged",
],
columns=columns,
source=rows,
style={"height": 420},
)
@callback(
Output("event-log", "children"),
Input("orders-grid", "eventData"),
prevent_initial_call=True,
)
def show_event(event):
return f'{event["sequence"]}: {event["name"]}'Names are deduplicated and listeners are reconciled when eventListeners
changes. A dedicated name is ignored in eventListeners because its dedicated
property already receives it.
Avoid high-frequency events such as viewportscroll unless a server request
for every event is intentional.
Use the complete Events API to choose event names and Event Patterns to understand their lifecycle.
Event serialization
Before calling Dash setProps, the bridge makes event details JSON safe:
- strings, finite numbers, booleans,
null, arrays, and plain objects remain; - JavaScript dates become ISO strings;
- non-finite numbers, cycles, DOM nodes, and class instances become
null; - function, symbol, and
undefinedobject fields are omitted; - unsupported array entries become
null.
Edit synchronization
Default: compact deltas
syncSourceOnEdit=False is the default. The browser applies the edit and sends
only the compact afteredit envelope to Python:
edit in browser -> compact afteredit envelope -> Dash callbackThis avoids cloning or transmitting the complete source and is recommended for large datasets, autosave APIs, patch queues, and audit logs.
@callback(
Output("save-status", "children"),
Input("orders-grid", "afteredit"),
prevent_initial_call=True,
)
def persist_edit(event):
detail = event["detail"]
if "prop" not in detail:
return "A range edit was received"
save_cell_change(
row_index=detail["rowIndex"],
field=detail["prop"],
value=detail["val"],
)
return f'Saved row {detail["rowIndex"]}'save_cell_change represents your persistence layer. For multi-user data,
include a stable identifier in every source row and map the reported row index
to that identifier.
Opt in to full source synchronization
Set syncSourceOnEdit=True only when a callback needs the complete updated
source:
grid = RevoGrid(
id="orders-grid",
syncSourceOnEdit=True,
columns=columns,
source=rows,
style={"height": 420},
)
@callback(
Output("row-count", "children"),
Input("orders-grid", "source"),
prevent_initial_call=True,
)
def receive_complete_source(source):
return f"{len(source)} rows received"The bridge snapshots source after every edit and updates it together with
afteredit. It suppresses the immediate echoed assignment when Dash returns
that same snapshot, avoiding a redundant grid reset.
Full synchronization costs memory, serialization time, and network bandwidth proportional to the complete dataset.
Complete component property reference
This is the complete public property surface generated for
dash_datagrid.RevoGrid.
Dash host and event bridge
| Property | Python shape | Purpose |
| --- | --- | --- |
| id | str or Dash pattern ID | Component identifier |
| className | str | CSS class on the Dash host |
| style | dict | Inline host style; normally include a height |
| afteredit | dict | Latest dedicated edit envelope |
| afterfocus | dict | Latest dedicated focus envelope |
| headerclick | dict | Latest dedicated header-click envelope |
| roworderchanged | dict | Latest row-order envelope |
| aftersortingapply | dict | Latest completed-sorting envelope |
| beforefilterapply | dict | Latest pre-filter notification |
| aftercolumnresize | dict | Latest resize envelope |
| eventListeners | list[str] | Other Core event names to observe |
| eventData | dict | Latest generic event envelope |
| syncSourceOnEdit | bool, default False | Also update full Dash source after edits |
Core data and schema
| Property | Python shape | Purpose | Documentation |
| --- | --- | --- | --- |
| columns | list[dict] | Regular/grouped column definitions | Columns |
| source | list[dict] | Main row source | Rows |
| pinnedTopSource | list[dict] | Top pinned rows | Pinned Rows |
| pinnedBottomSource | list[dict] | Bottom pinned rows | Pinned Rows |
| columnTypes | dict | Named JSON-safe column presets | Column Types |
| rowDefinitions | list[dict] | Per-row sizes | RowDefinition |
| additionalData | dict | Extra plain JSON context | Advanced Configuration |
Core layout and interaction
| Property | Python shape | Purpose | Documentation |
| --- | --- | --- | --- |
| rowHeaders | bool or dict | Row numbers or JSON-safe header options | Row Headers |
| colSize | number | Default column width | Grid Size |
| rowSize | number | Default row height | Row Height |
| rowClass | str | Row field containing its CSS class | Rows |
| resize | bool | Allow column resizing | Column Resize |
| autoSizeColumn | bool or dict | Automatic column sizing | Column Autosize |
| stretch | bool or str | Fill remaining horizontal space | Column Stretch |
| readonly | bool | Make the complete grid read-only | Editing |
| applyOnClose | bool | Apply an editor value on close | Editing |
| range | bool | Enable range selection | Selection API |
| useClipboard | bool or dict | Clipboard behavior | Clipboard |
| canFocus | bool | Allow grid cell focus | Advanced Configuration |
| canMoveColumns | bool | Enable column movement | Column Ordering |
| canDrag | bool | Allow native drag-and-drop | Row Ordering |
Core data features
| Property | Python shape | Purpose | Documentation |
| --- | --- | --- | --- |
| filter | bool or dict | Built-in filtering | Filtering |
| sorting | dict | External sorting configuration | Sorting |
| grouping | dict | Core row grouping | Row Grouping |
| trimmedRows | dict | Hidden physical main-row indexes | Rows and Trimming |
| exporting | bool | Enable Core export behavior | Export Plugin |
Core rendering and platform
| Property | Python shape | Purpose | Documentation |
| --- | --- | --- | --- |
| theme | str | Theme name | Themes |
| accessible | bool | Accessibility behavior | Accessibility |
| rtl | bool | Right-to-left layout | RTL |
| frameSize | number | Virtualization buffer | Performance |
| disableVirtualX | bool | Disable column virtualization | Performance |
| disableVirtualY | bool | Disable row virtualization | Performance |
| virtualX | list[str] | Column dimensions using X virtualization | Viewports |
| noHorizontalScrollTransfer | bool | Do not mirror horizontal viewport scroll | RevoGrid API |
| hideAttribution | bool | Hide attribution only with the required subscription | Attribution |
Use the generated Python documentation locally:
from dash_datagrid import RevoGrid
help(RevoGrid)For exact Core defaults and TypeScript types, open the RevoGrid component API.
Python boundary limitations
Explicitly excluded Core properties
| Property | Reason |
| --- | --- |
| editors | Contains editor constructors/classes |
| plugins | Contains plugin classes |
| focusTemplate | Contains a render function |
| jobsBeforeRender | Contains browser promises |
| registerVNode | Contains virtual nodes or render functions |
Functions nested inside otherwise supported objects are unsupported too. That includes custom cell/header renderers, custom editor constructors, custom comparators or parsers, custom filters, and custom grouping renderers.
before* events are notifications in Python
Cancelable RevoGrid before* events rely on a synchronous JavaScript listener
calling preventDefault(). A Python callback runs after a network round trip,
so beforefilterapply and generic before* callbacks cannot cancel or rewrite
the browser action.
Imperative methods are not exposed
The Core API documents selection, scrolling, data, export, and targeted-update methods for JavaScript. They are not callable through the Python component in v1. Use Dash output props for Python-driven changes.
Troubleshooting
Blank or zero-height grid
Set a height on the component host:
RevoGrid(..., style={"height": "60vh", "minHeight": 320})Value is not JSON serializable
Normalize DataFrame, NumPy, Decimal, datetime, UUID, and custom object values
before passing them to Dash.
Custom renderer/editor/comparator/filter is ignored
Those APIs require JavaScript functions and are outside the Python v1 boundary.
before* callback does not cancel the operation
Python callbacks cannot synchronously call the browser event's
preventDefault().
Large request after each edit
Leave syncSourceOnEdit=False and use the compact afteredit delta.
Generic event is not received
Use the lowercase name from the
Events API. Dedicated event names
update their named property instead of eventData.
Complete RevoGrid documentation map
The Dash component is generated from the Core metadata. These are the authoritative API and feature references:
| Area | Documentation |
| --- | --- |
| Complete component properties, events, methods | RevoGrid API |
| Complete event names and payloads | Events API |
| Event order and lifecycle | Event Patterns |
| Generated TypeScript type index | Type Reference |
| Columns | Column Guide, ColumnRegular, ColumnGrouping |
| Rows | Row Guide, Headers, Height, Pinning, Ordering |
| Editing, selection, clipboard | Editing, Selection API, Clipboard, Clipboard API |
| Filtering, sorting, grouping | Filtering, Sorting, Grouping |
| Sizing and column layout | Grid Size, Autosize, Resize, Stretch, Pin Columns, Column Order |
| Data ownership and remote data | Data Sync, Server-side Data, Real-time Updates |
| Virtualization | Performance, Viewports |
| Platform and appearance | Themes, Accessibility, RTL |
| Export | Export Plugin, Excel Export, PDF Export |
Some Core pages include browser-only functions and methods. Use the property tables and boundary section above to determine what the Dash package exposes.
npm package
The npm distribution is @revolist/dash-datagrid. It exports the generated
React bridge and bundles the standalone RevoGrid custom element. React and
ReactDOM remain peer dependencies. Python Dash applications should install the
PyPI package instead.
The Stencil Dash output target is private build tooling in the RevoGrid repository root. It is bundled into this package's generated JavaScript and is not a public npm package or runtime dependency.
Example application
examples/
is a complete project with its own dependency manifest, setup instructions,
responsive styling, a DataFrame-backed grid, Python-driven data changes, and
dedicated plus generic callbacks.
Run it from the repository:
cd examples
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python app.pyDevelopment
Development requires Node 22, npm, and Python 3.10 or newer:
npm install
python -m venv .venv
.venv/bin/python -m pip install -r requirements-dev.txt
npm run build
npm test
npm run test:browsernpm run build bundles the standalone custom elements with React externalized,
then runs the official dash-generate-components pipeline.
Release
The release workflow preflights the exact version on npm and PyPI, tests the npm tarball and Python distributions, publishes both registries, and creates the matching Git tag and GitHub release.
