Skip to main content

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?

  • No prop translation. The exact object you pass to AgGridReact in 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_KEY is present.
  • Exposes window.agGridShared with helpers (formatters, renderers, themes) and ensureConfigs() to create window.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 call setProps from 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.themeQuartz and themeAlpine (from AG Grid v35). Set theme on your config to use them.
  • Include AG Grid CSS separately in your Dash app (e.g., add ag-grid.css + a theme CSS to assets/ or link from a CDN).
  • Use your own CSS by applying className/style on the Dash component and custom cell renderers in the registry.

Enterprise support

  • Enterprise modules are registered automatically when available. Provide window.AGGRID_LICENSE_KEY before 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, or dashProps.
  • Charts: Use params.api.createRangeChart in 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.

  1. 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"
        }
    },
)
  1. 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 },
  };
};
  1. What the backend does
  • configArgs["ssrm"] triggers register_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 duckdb installed in your Dash environment.

Notes:

  • configArgs.ssrm.endpoint lets you customise the base path and avoid collisions.
  • Set filters automatically fetch distinct values from /distinct when you use filter: 'agSetColumnFilter'.
  • You can supply a builder callable instead of table if you need dynamic SQL.

Managing asset size

  • npm run build produces a minified bundle; ship the built assets, not npm start output.
  • Avoid loading both dash-ag-grid and dash-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 extras
    • make js-build - npm ci + bundle + backend stubs
    • make 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 to package.json and dash_aggrid_js/package-info.json via make 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:

  1. Ensure Node.js + npm are installed.
  2. 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
  1. Install the rebuilt package:
python -m build
pip install dist/dash_aggrid_js-*.whl

Notes:

  • --no-install skips npm install (use if node_modules/ is already up to date).
  • --skip-build updates versions without rebuilding the JS bundle.
  • You can also use env vars: AGGRID_VERSION and AGCHARTS_VERSION.
  • This rebuilds the bundled JS under dash_aggrid_js/ that Dash serves at runtime.

Testing

  • pytest dash-aggrid/tests runs 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 configKey is missing, the component renders a dashed placeholder instead of crashing.
  • Load order matters: keep shared helpers in 00-*, page registries in 01-*.
  • Avoid mixing with dash-ag-grid; CSS and event bridges will conflict.
  • Set filters with SSRM need a gridId in requests (automatically injected); don't override serverSideDatasource.getRows without preserving request.gridId.
  • Menu menuTabs validation only runs in dev mode; invalid tabs log warnings.

Migration checklist (dash-ag-grid -> AgGridJS)

  1. Move columnDefs/gridOptions into assets/aggrid/01-*.js under window.AGGRID_CONFIGS.
  2. Replace dash_ag_grid.AgGrid usages with dash_aggrid_js.AgGridJS(configKey=..., configArgs=...).
  3. Keep rowData in Dash only if you truly need Python-sourced data; otherwise move data fetching to the registry/DuckDB.
  4. Map callbacks:
    • selectedRows -> same
    • filterModel/sortModel -> same shape as AG Grid JS
    • Edits -> editedCells[0]
    • Cell clicks -> cellClicked
  5. Remove the dash-ag-grid CSS/JS bundles from assets/.

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; keep package.json, dash_aggrid_js/package-info.json, and package-lock.json in sync via make sync-version VERSION=X.Y.Z and a follow-up npm install.
  • Build assets + wheels locally with make dist (runs npm run build then python -m build).
  • Git tags of the form vX.Y.Z trigger the Publish workflow, which verifies the tag matches the version, rebuilds assets, runs tests, and publishes to PyPI using PYPI_API_TOKEN.
  • CI (CI workflow) runs lint/build/tests on PRs and pushes to main/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.md updated 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dash_aggrid_js-0.6.3.tar.gz (15.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dash_aggrid_js-0.6.3-py3-none-any.whl (15.9 MB view details)

Uploaded Python 3

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

Hashes for dash_aggrid_js-0.6.3.tar.gz
Algorithm Hash digest
SHA256 03e1e112cb53f56545c5b31f906f7f3b9f8989699ca172306e22711b55b0fbcf
MD5 2329d500fe5c8cf893c15a355ec96fe8
BLAKE2b-256 e6ab330d06f62f8925eda30a500d4e4accf9d5a905ac021583417cfebf311865

See more details on using hashes here.

Provenance

The following attestation bundles were made for dash_aggrid_js-0.6.3.tar.gz:

Publisher: release.yml on ScottTpirate/dash-aggrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for dash_aggrid_js-0.6.3-py3-none-any.whl
Algorithm Hash digest
SHA256 58e72a6efa6fd39d06e57ccc980cc09a9cd229c0f86e08bc6516667da8040a52
MD5 1b1434772f4175be570d9148db548811
BLAKE2b-256 482042f4fa1f91bf438119d228bce4fba9480bd0c1e723fe4bece476a89a3d17

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page