Tiny Dash wrapper that mounts AgGridReact using a JS config registry
Project description
dash-aggrid-js
dash-aggrid-js (Python import: dash_aggrid_js) is a deliberately thin Dash wrapper around AgGridReact. It mounts the AG Grid React component directly, so you can copy examples from the AG Grid docs, drop them into a browser-side config registry, and the grid just works inside Dash.
Warning: Pick one wrapper per app. AgGridJS is not meant to run alongside
dash-ag-grid; loading both introduces duplicate CSS, overlapping themes, and conflicting event glue. Choose one approach per Dash project.
Table of contents
- Why this wrapper?
- Quick start
- Installation
- Creating the config registry
- Using
AgGridJSin Dash - Dash props & event bridge
- Passing arguments (
configArgs) - Styling & theming
- Enterprise support
- Advanced patterns
- Server-side row model (SSRM)
- Managing asset size
- Developing the component
- Testing
- Known quirks
- Migration checklist (dash-ag-grid -> AgGridJS)
- AI assistant playbook
- Packaging & distribution
- FAQ
Why this wrapper?
- No prop translation. The exact object you pass to
AgGridReactin the docs is what the wrapper forwards. - Pure JavaScript configs. Keep row models, value formatters, renderers, and callbacks in JavaScript without serialising through Python.
- Minimal Dash glue. Only selection, filters, sorting, edits, and cell clicks are mirrored back to Dash for callbacks.
- AG Grid v35.x / AG Charts v13.x compatible (Community + optional Enterprise).
Quick start
Clone the repo and run the sample app:
git clone https://github.com/ScottTpirate/dash-aggrid.git
cd dash-aggrid
make install-dev
make js-build
python3 app.py
Visit http://127.0.0.1:8050 for the sample app. demo_app.py renders three grids (including an integrated chart) and a standalone AG Charts example powered by AgChartsJS. The configs live in assets/aggrid-configs.js and assets/agcharts-configs.js.
Reusing in another project? Install the package into that Dash app, copy the asset registry, and you're ready to go.
Installation
From PyPI (when published)
pip install dash-aggrid-js
From source
cd dash-aggrid
make install-dev
make js-build # installs JS deps, builds the JS bundle, and regenerates the backends
Creating the config registry
AgGridJS looks for configs on window.AGGRID_CONFIGS. Each entry can be an object or a (context) => config factory. Recommended layout (no bundler required):
1) Shared bootstrap (assets/aggrid/00-aggrid-shared.js)
- Applies the AG Grid Enterprise license if
window.AGGRID_LICENSE_KEYis present. - Exposes
window.agGridSharedwith helpers (formatters, renderers, themes) andensureConfigs()to createwindow.AGGRID_CONFIGS.
/* eslint-disable no-console */
(function initAgGridShared() {
if (typeof window === "undefined") return;
// License (optional)
const key = window.AGGRID_LICENSE_KEY;
if (key && window.agGrid?.LicenseManager) {
try {
window.agGrid.LicenseManager.setLicenseKey(key);
} catch (err) {
console.error("License apply failed", err);
}
}
const shared = (window.agGridShared = window.agGridShared || {});
Object.assign(shared, {
themes: window.AgGridJsThemes || {},
formatInteger(value) {
const fmt = new Intl.NumberFormat("en-US");
return typeof value === "number" && Number.isFinite(value) ? fmt.format(value) : "";
},
ensureConfigs() {
window.AGGRID_CONFIGS = window.AGGRID_CONFIGS || {};
return window.AGGRID_CONFIGS;
},
});
})();
2) One file per page (assets/aggrid/01-<page_name>.js)
- Register all grids for that page in one file; use helpers from
agGridShared.
/* eslint-disable no-console */
(function registerExamplePageGrids() {
if (typeof window === "undefined") return;
const shared = window.agGridShared || {};
const configs =
shared.ensureConfigs?.() || (window.AGGRID_CONFIGS = window.AGGRID_CONFIGS || {});
configs["example-grid"] = function exampleGrid(context = {}) {
const quartzTheme = shared.themes?.themeQuartz;
const rowData =
Array.isArray(context?.dashProps?.rowData) ? context.dashProps.rowData : context?.rowData || [];
return {
rowData,
columnDefs: [
{ headerName: "Name", field: "name" },
{ headerName: "Value", field: "value" },
],
defaultColDef: { sortable: true, resizable: true, filter: false },
theme: quartzTheme,
};
};
})();
Load order is alphabetical, so 00-* runs before any 01-* page bundles.
Using AgGridJS in Dash
from dash import Dash, html, Output, Input
from dash_aggrid_js import AgGridJS
app = Dash(__name__) # serves ./assets automatically
app.layout = html.Div(
[
AgGridJS(
id="inventory-grid",
configKey="example-grid", # must match the JS registry entry
style={"height": 420},
configArgs={"locale": "en-US"}, # available to the JS factory
),
html.Pre(id="selection"),
]
)
@app.callback(Output("selection", "children"), Input("inventory-grid", "selectedRows"))
def show_selection(rows):
return f"Selected rows: {rows or []}"
if __name__ == "__main__":
app.run(debug=True)
rowData passed from Dash overrides any rowData set in the JS config.
Dash props & event bridge
AgGridJS relays key events to Dash via setProps (only when explicitly allowed):
- Always:
selectedRows,filterModel,sortModel. - Opt-in (set
registerProps=["cellClicked","editedCells"]or pass the prop):cellClicked,editedCells. - Custom: add any prop name to
registerProps(e.g.,["cellDoubleClicked"]) and callsetPropsfrom your asset.
Example: keep filters/selection in sync with the URL or callbacks:
@app.callback(
Output("summary", "children"),
Input("inventory-grid", "selectedRows"),
Input("inventory-grid", "filterModel"),
Input("inventory-grid", "sortModel"),
)
def summarize(rows, filters, sorts):
return {
"selected": len(rows or []),
"filters": filters,
"sorts": sorts,
}
To push a filter model from Dash into the grid, set the filterModel prop; the component will apply it and fire the usual filterChanged events.
Passing arguments (configArgs)
configArgs is JSON-serialisable and passed to your registry factory as context.configArgs. The factory also receives { id, dashProps, setProps }.
Example: reuse one config for multiple locales and default row sets:
configs["sales-grid"] = function salesGrid(context = {}) {
const locale = context.configArgs?.locale || "en-US";
const theme = window.agGridShared?.themes?.themeQuartz;
const rowData = context.configArgs?.rows || context?.dashProps?.rowData || [];
return {
rowData,
columnDefs: [
{ field: "region", rowGroup: true },
{
field: "revenue",
valueFormatter: (p) => Intl.NumberFormat(locale, { style: "currency", currency: "USD" }).format(p.value || 0),
},
],
defaultColDef: { sortable: true, resizable: true, filter: true },
theme,
};
};
Registering extra Dash props (registerProps)
Dash only accepts callbacks for props declared on the component. Use registerProps to opt into event props you plan to emit from your assets (e.g., cellDoubleClicked), and gate the built-in click/edit emissions:
AgGridJS(
id="orders-grid",
configKey="orders",
registerProps=["cellClicked", "cellDoubleClicked", "editedCells"],
)
Then in your asset:
onCellDoubleClicked(event) {
setProps?.({
cellDoubleClicked: {
colId: event?.column?.getColId(),
value: event?.value,
data: event?.data || event?.node?.aggData || null,
},
});
}
If you don't register a prop, AgGridJS won't emit it.
To avoid repeating the same list everywhere, set defaults once at app bootstrap:
from dash_aggrid_js import set_default_props
set_default_props(["cellDoubleClicked"])
Defaults are merged into each grid's registerProps unless you explicitly pass your own list.
Styling & theming
- AgGridJS registers
window.AgGridJsThemes.themeQuartzandthemeAlpine(from AG Grid v35). Setthemeon your config to use them. - Include AG Grid CSS separately in your Dash app (e.g., add
ag-grid.css+ a theme CSS toassets/or link from a CDN). - Use your own CSS by applying
className/styleon the Dash component and custom cell renderers in the registry.
Enterprise support
- Enterprise modules are registered automatically when available. Provide
window.AGGRID_LICENSE_KEYbefore the registry loads to avoid watermarks:
// assets/license.js
window.AGGRID_LICENSE_KEY = "<YOUR LICENSE KEY>";
- Keep enterprise-only props in the registry (row grouping, pivoting, charts, etc.); the wrapper doesn't block them.
Advanced patterns
- Shared access to grid API:
window.AgGridJsRegistry.getApiAsync(<gridId>)resolves the grid API once ready. - User + Dash handlers: Registry callbacks still run; AgGridJS wraps them and then mirrors events back to Dash.
- Config factories: Because configs are functions, you can branch on
configArgs, feature flags, ordashProps. - Charts: Use
params.api.createRangeChartin registry callbacks (onFirstDataRendered) to mount AG Charts into a provided container.
Server-side row model (SSRM)
AgGridJS ships helpers to register DuckDB-backed SSRM endpoints automatically.
- Dash usage
AgGridJS(
id="orders-grid",
configKey="ssrm-grid",
style={"height": 520},
configArgs={
"ssrm": {
"duckdb_path": "ssrm_demo.duckdb", # required
"table": "orders", # table/view or subquery
# optional: "endpoint": "/custom/ssrm/orders"
}
},
)
- Registry entry (simplified from
assets/aggrid-configs.js)
configs["ssrm-grid"] = function ssrmGrid(context = {}) {
const gridId = context.id || "ssrm-grid";
const ssrmArgs = context.configArgs?.ssrm || {};
const baseEndpoint = String(ssrmArgs.endpoint || "_aggrid/ssrm").replace(/\/$/, "");
const datasource = {
getRows(params) {
fetch(`${baseEndpoint}/${encodeURIComponent(gridId)}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params.request || {}),
})
.then((r) => r.json())
.then((payload) =>
params.success({
rowData: Array.isArray(payload.rows) ? payload.rows : [],
rowCount: typeof payload.rowCount === "number" ? payload.rowCount : undefined,
})
)
.catch((err) => {
console.error("AgGridJS SSRM request failed", err);
params.fail();
});
},
};
return {
rowModelType: "serverSide",
serverSideDatasource: datasource,
columnDefs: [
{ field: "region", filter: "agSetColumnFilter", rowGroup: true },
{ field: "product", filter: "agSetColumnFilter" },
{ field: "units", type: "numericColumn", aggFunc: "sum" },
],
defaultColDef: { sortable: true, filter: true, resizable: true, enableRowGroup: true, enablePivot: true },
};
};
- What the backend does
-
configArgs["ssrm"]triggersregister_duckdb_ssrm(Python) to expose routes under_aggrid/ssrm/<gridId>plus/distinct/<col>for set filters. Use relative paths so app prefixes (e.g., Dash Enterprise) are preserved. -
To avoid “No SSRM configuration registered for grid …” in multi‑worker deployments, eagerly register your SSRM grids at import time (once per process):
# content/foo/service.py from dash_aggrid_js import register_duckdb_ssrm DUCKDB_PATH = "/mount/foo/foo.duckdb" TABLE = "(SELECT * FROM foo) AS foo_ssrm" def _register_ssrm(): cfg = {"duckdb_path": DUCKDB_PATH, "table": TABLE} register_duckdb_ssrm("foo-grid", cfg) # if you forward filterModel from another grid cfg_drill = dict(cfg, forwardFilterModel=True) register_duckdb_ssrm("foo-drilldown-grid", cfg_drill) _register_ssrm()
This ensures every worker process has the SSRM registry populated before the first datasource request arrives; retries become a safety net rather than a requirement.
-
Incoming SSRM datasource requests are translated into DuckDB SQL via
dash_aggrid_js.ssrm.sql_for. -
Requires
duckdbinstalled in your Dash environment.
Notes:
configArgs.ssrm.endpointlets you customise the base path and avoid collisions.- Set filters automatically fetch distinct values from
/distinctwhen you usefilter: 'agSetColumnFilter'. - You can supply a
buildercallable instead oftableif you need dynamic SQL.
Managing asset size
npm run buildproduces a minified bundle; ship the built assets, notnpm startoutput.- Avoid loading both
dash-ag-gridanddash-aggrid-js; duplicate CSS/JS bloats downloads. - Keep registry files focused (one per page) to minimise parse/exec time.
Developing the component
- Common commands (see
Makefile):make install-dev- install Python dev+test extrasmake js-build-npm ci+ bundle + backend stubsmake lint- pre-commit (ruff + black)make test- pytest (requires Chrome/Chromedriver for the UI test)make dist- rebuild JS then build wheel/sdist
- Version source of truth:
dash_aggrid_js/__about__.py(sync topackage.jsonanddash_aggrid_js/package-info.jsonviamake sync-version VERSION=X.Y.Z).
Custom AG Grid versions (build-time)
AG Grid is bundled into the JS build, so switching versions is a build-time operation. The easiest way to do this is the built-in CLI:
- Ensure Node.js + npm are installed.
- Run the build command from a source checkout of this repo.
Examples:
# Pin both AG Grid + AG Charts
python -m dash_aggrid_js.build --aggrid 35.2.0 --agcharts 13.2.0
# Use "latest" for both
python -m dash_aggrid_js.build --latest
- Install the rebuilt package:
python -m build
pip install dist/dash_aggrid_js-*.whl
Notes:
--no-installskipsnpm install(use ifnode_modules/is already up to date).--skip-buildupdates versions without rebuilding the JS bundle.- You can also use env vars:
AGGRID_VERSIONandAGCHARTS_VERSION. - This rebuilds the bundled JS under
dash_aggrid_js/that Dash serves at runtime.
Testing
pytest dash-aggrid/testsruns a headless browser smoke test that ensures demo grids render rows. It skips if Chrome/Chromedriver is unavailable.- Add Dash callback-level tests by following the pattern in
tests/test_usage.py(ThreadedRunner + Browser).
Known quirks
- If
configKeyis missing, the component renders a dashed placeholder instead of crashing. - Load order matters: keep shared helpers in
00-*, page registries in01-*. - Avoid mixing with
dash-ag-grid; CSS and event bridges will conflict. - Set filters with SSRM need a
gridIdin requests (automatically injected); don't overrideserverSideDatasource.getRowswithout preservingrequest.gridId. - Menu
menuTabsvalidation only runs in dev mode; invalid tabs log warnings.
Migration checklist (dash-ag-grid -> AgGridJS)
- Move columnDefs/gridOptions into
assets/aggrid/01-*.jsunderwindow.AGGRID_CONFIGS. - Replace
dash_ag_grid.AgGridusages withdash_aggrid_js.AgGridJS(configKey=..., configArgs=...). - Keep
rowDatain Dash only if you truly need Python-sourced data; otherwise move data fetching to the registry/DuckDB. - Map callbacks:
selectedRows-> samefilterModel/sortModel-> same shape as AG Grid JS- Edits ->
editedCells[0] - Cell clicks ->
cellClicked
- Remove the
dash-ag-gridCSS/JS bundles fromassets/.
AI assistant playbook
- Do not invent props; use AG Grid docs that match the bundled major version (currently v35) and the registry pattern shown above.
- Keep complex logic in JS assets; pass only JSON-friendly state through Dash props.
- Prefer set filters, row grouping, and SSRM examples from AG Grid's official guides; adapt minimally.
- When editing docs or code, avoid mixing this wrapper with
dash-ag-grid.
Packaging & distribution
- Version lives in
dash_aggrid_js/__about__.py; keeppackage.json,dash_aggrid_js/package-info.json, andpackage-lock.jsonin sync viamake sync-version VERSION=X.Y.Zand a follow-upnpm install. - Build assets + wheels locally with
make dist(runsnpm run buildthenpython -m build). - Git tags of the form
vX.Y.Ztrigger thePublishworkflow, which verifies the tag matches the version, rebuilds assets, runs tests, and publishes to PyPI usingPYPI_API_TOKEN. - CI (
CIworkflow) runs lint/build/tests on PRs and pushes tomain/master.
Recommended release flow:
make sync-version VERSION=0.6.3
make js-build
make test
make dist
git commit -am "Release 0.6.3"
git tag v0.6.3
git push origin main
git push origin v0.6.3
- Keep
CHANGELOG.mdupdated for anything user-facing.
FAQ
Is this the same as dash-ag-grid?
No. dash-aggrid-js mounts AgGridReact directly and keeps complex logic in JavaScript assets; dash-ag-grid translates props in Python. Use one per Dash app to avoid CSS/event conflicts.
Do I need a bundler?
No. Drop the generated bundle plus your config registry into assets/ and Dash will serve them. Use npm run build only when developing the component itself or updating bundled assets.
How do I access the grid API from elsewhere?
Use window.AgGridJsRegistry.getApiAsync("<gridId>") in your own scripts to get the API once the grid is ready.
Can I use SSRM without DuckDB?
You can, but the built-in helpers expect DuckDB. Provide your own serverSideDatasource.getRows in the registry and omit configArgs.ssrm if you're targeting another backend.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dash_aggrid_js-0.6.3.tar.gz.
File metadata
- Download URL: dash_aggrid_js-0.6.3.tar.gz
- Upload date:
- Size: 15.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03e1e112cb53f56545c5b31f906f7f3b9f8989699ca172306e22711b55b0fbcf
|
|
| MD5 |
2329d500fe5c8cf893c15a355ec96fe8
|
|
| BLAKE2b-256 |
e6ab330d06f62f8925eda30a500d4e4accf9d5a905ac021583417cfebf311865
|
Provenance
The following attestation bundles were made for dash_aggrid_js-0.6.3.tar.gz:
Publisher:
release.yml on ScottTpirate/dash-aggrid
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dash_aggrid_js-0.6.3.tar.gz -
Subject digest:
03e1e112cb53f56545c5b31f906f7f3b9f8989699ca172306e22711b55b0fbcf - Sigstore transparency entry: 1217648343
- Sigstore integration time:
-
Permalink:
ScottTpirate/dash-aggrid@07b0bad5b16a5766dadfbe3eaac121bf707ecd6d -
Branch / Tag:
refs/tags/v0.6.3 - Owner: https://github.com/ScottTpirate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@07b0bad5b16a5766dadfbe3eaac121bf707ecd6d -
Trigger Event:
push
-
Statement type:
File details
Details for the file dash_aggrid_js-0.6.3-py3-none-any.whl.
File metadata
- Download URL: dash_aggrid_js-0.6.3-py3-none-any.whl
- Upload date:
- Size: 15.9 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58e72a6efa6fd39d06e57ccc980cc09a9cd229c0f86e08bc6516667da8040a52
|
|
| MD5 |
1b1434772f4175be570d9148db548811
|
|
| BLAKE2b-256 |
482042f4fa1f91bf438119d228bce4fba9480bd0c1e723fe4bece476a89a3d17
|
Provenance
The following attestation bundles were made for dash_aggrid_js-0.6.3-py3-none-any.whl:
Publisher:
release.yml on ScottTpirate/dash-aggrid
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dash_aggrid_js-0.6.3-py3-none-any.whl -
Subject digest:
58e72a6efa6fd39d06e57ccc980cc09a9cd229c0f86e08bc6516667da8040a52 - Sigstore transparency entry: 1217648363
- Sigstore integration time:
-
Permalink:
ScottTpirate/dash-aggrid@07b0bad5b16a5766dadfbe3eaac121bf707ecd6d -
Branch / Tag:
refs/tags/v0.6.3 - Owner: https://github.com/ScottTpirate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@07b0bad5b16a5766dadfbe3eaac121bf707ecd6d -
Trigger Event:
push
-
Statement type: