AG Grid cell styling
This is the most reliably confusing area in NiceGUI’s AG Grid integration. Here is the full decision tree.What works
| Goal | Mechanism | Notes |
|---|---|---|
| Same style on every cell in a column | "cellStyle": {"color": "#f00"} | Plain dict; AG Grid applies as-is |
| Per-cell dynamic colour / content | html_columns + pre-built HTML | The only reliable dynamic option |
| Column fills remaining width | "flex": 1 on the column + "autoSizeStrategy": None on the grid | flex is ignored when autoSizeStrategy is set (NiceGUI default) |
What does NOT work
| Mechanism | Why it fails |
|---|---|
"cellStyle": {"function": "params => ..."} | NiceGUI serialises grid options as JSON. The {"function":"..."} wrapper is not converted to a live JS function; AG Grid receives an inert object and ignores it. |
"cellClassRules": {"my-class": "params.value === 'X'"} | Same root cause — the expression string is never evaluated; no class is ever added. |
Dynamically computed CSS variable names in JS ('var(--color-' + idx + ')') | Browsers do not resolve CSS custom-property names that are constructed by string concatenation in JavaScript inline styles. |
The html_columns pattern (correct approach)
Pre-render styled HTML in Python when building row data. Keep raw values in separate
fields for filtering and sorting. Enable HTML rendering per column index with
html_columns.
AG Grid data and updates
Grid stays empty even though data is fetched successfully
Grid stays empty even though data is fetched successfully
grid.update() sends the new options to the currently connected
WebSocket clients. If it is called synchronously during page construction, the browser
has not yet received and mounted the component, so the update is silently discarded.Fix: Pre-load data before creating the grid and pass it directly to the
constructor. This means the correct rowData is part of the initial render payload
and never needs an early update.grid.options["rowData"] = rows; grid.update().Method 'setRowData' not found (browser console)
Method 'setRowData' not found (browser console)
Method "setRowData" not found (repeated). Grid does
not update.Wrong pattern:setRowData was removed in AG Grid v28+. The version bundled with
NiceGUI does not expose this method.Fix: Mutate grid.options directly and call grid.update():Column sort order resets every time new rows arrive
Column sort order resets every time new rows arrive
grid.update() re-sends the full grid.options dict to the client,
including columnDefs without any sort state. AG Grid resets its internal column state
to match the incoming options, wiping the sort the user applied.Fix: Re-apply the desired sort immediately after every grid.update() call using
applyColumnState:Column resizes on every row append or filter change
Column resizes on every row append or filter change
autoSizeStrategy on every ui.aggrid instance.
Every grid.update() re-sends the full options object, which re-triggers the strategy
and causes AG Grid to auto-resize all columns.Fix: Disable the strategy in the grid options. Columns then respect only their
width / minWidth / flex definitions and never auto-resize:'colDef.flex is not supported with gridOptions.autoSizeStrategy' (browser console)
'colDef.flex is not supported with gridOptions.autoSizeStrategy' (browser console)
flex on column definitions. The column does
not expand to fill remaining space.Root cause: NiceGUI’s default autoSizeStrategy conflicts with column-level flex.
AG Grid logs a warning and ignores flex when autoSizeStrategy is present.Fix: Set "autoSizeStrategy": None on the grid (see entry above), then add flex
to whichever column should fill the remaining space:grid.run_grid_method() returns AwaitableResponse, not a coroutine
grid.run_grid_method() returns AwaitableResponse, not a coroutine
TypeError: a coroutine was expected, got <AwaitableResponse ...> when
wrapping a grid method call in asyncio.create_task() or await.Wrong pattern:grid.run_grid_method() returns a NiceGUI AwaitableResponse, which is
an awaitable but not a coroutine. asyncio.create_task() requires a coroutine and
raises TypeError when handed anything else.In practice, run_grid_method sends the command to the browser immediately (fire-and-
forget). You do not need to await it for scrolling or applying column state.Fix: Call it as a plain synchronous statement from a regular def function:grid API not available when called during page construction
grid API not available when called during page construction
grid.run_grid_method(...) synchronously during page construction
has no effect (e.g. initial sort is not applied). No error is raised.Root cause: run_grid_method sends a JavaScript call to the browser-side AG Grid
instance. During server-side page construction the browser has not yet received or
mounted the component, so the call is sent to a non-existent grid and silently dropped.Fix: Defer any grid API calls that must run after mount using a one-shot timer:0.1 s is enough for the WebSocket round-trip and component mount in a local
environment. Increase if deploying to a slower network.NiceGUI UI element state
Button chip can be toggled off but never back on
Button chip can be toggled off but never back on
Persisted chip state does not restore correctly on page load
Persisted chip state does not restore correctly on page load
NiceGUI event handlers
Search input on_value_change handler raises AttributeError or silently receives wrong value
Search input on_value_change handler raises AttributeError or silently receives wrong value
None.Wrong pattern:on_value_change callback receives a ValueChangeEventArguments
object. The new value is on e.value, not e.args[0] or the object itself.Fix:hasattr guard also protects against edge cases where the event object shape
changes (e.g. clearable input firing a clear event with a slightly different signature).Page lifecycle and timers
RuntimeError: 'The parent slot of the element has been deleted' after navigating away
RuntimeError: 'The parent slot of the element has been deleted' after navigating away
Log file reading
Log files not found at expected path when launched via debugpy / launch.json
Log files not found at expected path when launched via debugpy / launch.json
launch.json).Root cause: debugpy changes the process working directory to the project root
configured in launch.json, which may differ from the directory where the server
normally runs. Any relative log-file path resolves differently depending on launch
method.Fix: Always resolve log paths to absolute paths using the configured workspace
directory, never relative to os.getcwd():Path in the
shared state object during server startup, before any UI page is rendered.