Accessor cheat sheet
Generated from shinywidgets/_accessors.py, each renderer’s _accessor_specs (in the per-library shinywidgets/_<lib>.py modules), and the accessor methods on each render_* class in shinywidgets/_render_widget.py. Every accessor attaches the moment its output renders and records interactions whether or not anything has read it yet – there is no warm-up idiom and no “first read” gotcha (see “The mental model”).
Columns, straight from each AccessorSpec (not from memory):
- Before any interaction: what the accessor reads before the user has ever done the thing it reports –
cancels (like req(False): nothing to check, the output just doesn’t render), None, []/{}, or a real seeded value.
- Across re-render: whether a re-render keeps the last interaction (the payload may then describe the previous render’s widget – see “Across re-renders”) or re-seeds from the newly rendered widget’s own current state.
- Default rate: a blank means the accessor declares no policy, so the renderer’s
rate_policy/rate_delay kwargs are a no-op for it (see “Rate limiting”).
Renderers with no accessors (use reactive_read() on .widget instead): render_widget, render_drawdata, render_ipysigma, render_geolibre, render_keplergl, render_ipycanvas, render_nglview, render_obsplot, render_holoviews, render_ipyfilechooser, render_pygwalker, render_vizarr, render_pyvista, render_vitessce, and render_pydeck (deprecated; pydeck >= 0.9 has no widget to read from – see render_maplibre). render_panel has one dynamic accessor, param(name) (below).
render_altair
selected(name=None) |
IntervalSelection \| PointSelection \| IndexSelection |
seeded (empty selection; never None) |
re-seeded |
debounce 250ms |
selected_rows(name=None, *, df=None) |
a data frame (same type as df) |
empty frame |
derived from selected |
debounce 250ms |
render_embedding_atlas
predicate() |
Optional[str] |
None (unfiltered) |
re-seeded |
debounce 250ms |
selected() |
native frame (pandas/polars) |
all rows |
derived from predicate |
debounce 250ms |
render_ipyaladin
clicked_object() |
Optional[dict] (ra, dec, data) |
None |
re-seeded |
none |
selected() |
List[astropy.Table] |
[] |
re-seeded |
none |
target() |
SkyCoord \| (Longitude, Latitude) |
seeded (construction-time center) |
re-seeded |
none |
fov() |
astropy.Angle |
seeded (construction-time fov) |
re-seeded |
none |
render_ipyleaflet / render_leafmap
Both share _IpyleafletAccessors; on render_leafmap, calling any of these on a non-ipyleaflet backend (maplibre, plotly, …) raises TypeError pointing at reactive_read().
bounds() |
((south, west), (north, east)) |
cancels until the browser’s first report |
re-seeded |
none |
zoom() |
float |
seeded (construction-time zoom) |
re-seeded |
none |
center() |
[lat, lng] |
seeded (construction-time center) |
re-seeded |
none |
clicked() |
Optional[dict] (coordinates: [lat, lng]) |
None |
kept |
none |
drawn_features() |
List[GeoJSON Feature dict] |
[], or raises TypeError if the map has no DrawControl |
re-seeded |
none |
render_itables
selected_rows() |
List[int] |
[]. Requires select=True |
re-seeded |
none |
render_jscatter
selected() |
numpy.ndarray[uint32] |
cancels until the browser’s first report |
re-seeded |
none |
selected_rows(df=None) |
the selected rows of df (default: the Scatter’s data frame), same type as df |
empty frame (cancels when selected() does) |
derived from selected |
none |
render_k3d
camera() |
List[float] (9 floats: position, target, up) |
cancels until the browser auto-fits (forever with camera_auto_fit=False and no explicit camera) |
re-seeded |
none (k3d itself throttles to ~1/200ms) |
render_ipycytoscape
clicked_node() |
Optional[dict] (ele.json()) |
None |
kept |
none |
clicked_edge() |
Optional[dict] (ele.json()) |
None |
kept |
none |
selected_nodes() |
List[dict] (node data) |
[] |
re-seeded |
debounce 250ms |
selected_edges() |
List[dict] (edge data) |
[] |
re-seeded |
debounce 250ms |
Renamed from node_clicked()/edge_clicked() (#441, naming convention: interaction first).
render_bqplot
selected() |
List[Optional[np.ndarray]], aligned with fig.marks |
seeded (None per mark) |
re-seeded |
debounce 250ms |
selected_rows(df, *, mark=None) |
the rows of df (required; same type as df) at one mark’s selected positions |
empty frame (cancels when selected() does) |
derived from selected |
debounce 250ms |
brush() |
Optional[np.ndarray \| dict] |
None |
re-seeded |
debounce 250ms |
No clicked(): element_click messages never reach marks while the figure has an interaction (its overlay intercepts clicks) – deliberately omitted rather than ship an accessor that’s silently dead in the common case.
render_ipyvolume
camera() |
live pythreejs.Camera |
cancels while the figure has no camera |
re-seeded |
none |
render_lonboard
view_state() |
MapViewState \| GlobeViewState |
seeded (computed from the layers at construction) |
re-seeded |
debounce 250ms (#400) |
selected() |
Optional[int] |
None |
re-seeded (seeded from whichever layer retains a pick on its own selected_index trait – None again for freshly built layers) |
none |
selected_rows(df=None) |
the picked row of df (default: rebuilt from the single pickable layer’s own data; raises with several), in df’s own type |
empty frame |
derived from selected() |
none |
selected_bounds() |
Optional[(minx, miny, maxx, maxy)] |
None |
re-seeded |
none |
clicked() |
Optional[(lng, lat)] |
None. Reading it is what turns on click reporting in the browser |
kept |
none |
[up] GlobeView permanently breaks view_state() (and reactive_read() on the trait): the browser sends pitch/bearing, which GlobeViewState has no fields for, so the first sync throws (swallowed) and it never updates.
render_maplibre
view_state() |
{center, zoom, bounds, bearing, pitch} |
cancels (req(False)) until the browser’s first report (the map’s load event) |
kept until the new map reports, then re-seeded |
none (the browser reports once per completed gesture, not per frame) |
clicked() |
Optional[{lng, lat}] |
None |
kept |
none |
drawn_features() |
list of drawn GeoJSON features |
[] |
re-seeded (the new map’s empty set) |
none |
render_matplotlib / render_plotnine
clicked() |
Optional[matplotlib.backend_bases.MouseEvent] |
None |
kept |
none |
picked() |
Optional[matplotlib.backend_bases.PickEvent] |
None. (render_matplotlib only.) |
kept |
none |
limits(ax=0) |
((x0, x1), (y0, y1)) |
seeded (current view limits) |
re-seeded |
debounce 250ms |
Every press fires clicked() (no dedup – MouseEvent has no equality); a double-click is 3 presses, not 2. render_plotnine has only clicked().
render_bokeh
selected() |
List[int] |
[] (never None – #437) |
re-seeded |
debounce 250ms |
selected_rows() |
pd.DataFrame |
empty frame |
derived from selected + source_data trigger |
debounce 250ms |
tapped() |
Optional[bokeh.events.Tap] |
None |
kept |
none |
zoomed() |
Optional[bokeh.events.RangesUpdate] |
None until the first gesture (an auto-fitting or categorical range has nothing to seed) |
re-seeded |
none |
[up] jupyter_bokeh fires the underlying on_change/on_event callback twice per browser change; the accessors’ input-priority dedup absorbs this (counted once), but a raw source.selected.on_change(...) callback you register yourself will see two calls.
render_perspective
filter() |
List[[col, op, value]] |
[] |
re-seeded |
none |
view_config() |
Dict (plugin, columns, group_by, split_by, aggregates, sort, filter, expressions) |
seeded (construction-time config) |
re-seeded |
none |
Row clicks/selections never reach Python.
render_ipydatagrid
selected() |
List[{"r1","r2","c1","c2"}] |
[] |
re-seeded |
debounce 250ms (#400) |
clicked_cell() |
Optional[dict] |
None |
kept |
none |
edited_cell() |
Optional[dict] |
None |
kept |
none |
visible_data() |
pd.DataFrame |
the whole frame |
re-seeded (derived trigger) |
none |
Renamed from cell_clicked() (#441). selected() now debounces by default (#400) – it used to be the canonical “no policy” example in this article; see “Rate limiting” for the current one.
render_plotly
clicked() |
Optional[pd.DataFrame] |
None |
kept |
none |
selected() |
Optional[pd.DataFrame] |
None |
kept (the new plot’s own empty selection replaces it once it reports – see note) |
none |
selecting() |
Optional[pd.DataFrame] |
None |
kept |
debounce 250ms |
brushed() |
Optional[dict] (range or lassoPoints) |
None |
kept (same note as selected()) |
none |
brushing() |
Optional[dict] |
None |
kept |
debounce 250ms |
relayout() |
Optional[dict] |
None |
kept (a delta payload; refers to the previous render’s plot) |
none |
hover() |
Optional[pd.DataFrame] |
None |
kept |
debounce 250ms |
double_clicked() |
Optional[bool] |
None |
kept (pulse: re-fires readers on every subsequent double-click even though the value stays True) |
none |
legend_clicked() |
Optional[pd.DataFrame] |
None |
kept (pulse: re-fires on every click, even of the same item) |
none |
visible_traces() |
Optional[pd.DataFrame] |
seeded (the figure’s initial trace visibility) |
re-seeded |
none |
selected_rows(df, *, key=None) |
same frame type as df |
empty frame |
derived from selected |
none |
selected()/brushed() attach at render like every other accessor (#419): a box or lasso selection made before the first read is reported, not lost. They don’t reset to None on their own re-render the instant it happens – plotly.js’s bridge also rebuilds and re-sends the new graph div’s own (initially empty) selection shortly after mount, which is what actually clears them; until that arrives, the old value is still what reads back. double_clicked()/legend_clicked() are pulses (priority="event"): identical values still re-fire readers, unlike every other accessor here, which dedupes. [docs]: a plain click also resets selected()/brushed() when dragmode="select"/"lasso" (a zero-area selection) – affects apps combining click-detail with lasso on one figure.
render_pythreejs
camera() |
live pythreejs.Camera |
seeded (the Python-constructed camera; pythreejs doesn’t sync at mount) |
re-seeded |
none |
Same shape as render_ipyvolume’s camera().
render_quak
sql() |
str |
cancels while the frontend is still initializing (empty string) |
re-seeded |
debounce 250ms |
data() |
lazy duckdb.DuckDBPyRelation |
the construction query’s rows |
derived from sql (reads the published, not live, query) |
debounce 250ms (inherited) |
render_rerun
selected() |
Optional[rerun.notebook.SelectionChangeEvent] |
None until the user first selects something |
kept (the new viewer’s load-time empty report is ignored) |
none |
render_mols2grid
selected() |
Optional[Dict[int, str]] ({row_index: SMILES}) |
{} |
re-seeded |
none |
clicked() |
Optional[dict] |
None. Only exists with clicks=True or a Python callback |
kept |
none |
Clicking the same molecule twice doesn’t re-fire clicked() (a mols2grid limitation: the trait only changes when the payload does – unlike plotly’s clicked(), there’s no underlying event stream to re-publish from).
render_niivue
location() |
Optional[dict] (ax_cor_sag, frac, mm, vox, values, string, xy) |
None (not sent on load) |
re-seeded |
debounce 250ms |
render_reactable
All five attach at render and seed from the browser’s own getState() response (so a sort or selection made before the first read is reflected), confirmed working on current main – the _accessor_specs typo that broke every one of these (missing leading underscore, shinywidgets/_render_widget.py) is fixed.
selected_rows() |
List[int] |
cancels until the browser first reports |
re-seeded |
none |
page() |
int |
cancels until the browser first reports |
re-seeded |
none |
sorted() |
List[{"id","desc"}] |
cancels until the browser first reports |
re-seeded |
none |
filtered() |
List[{"id","value"}] |
cancels until the browser first reports |
re-seeded |
debounce 250ms |
search() |
str |
cancels until the browser first reports |
re-seeded |
debounce 250ms |
All five share “0-based, data-ordered positions survive sort/filter/paging” for selected_rows() and page()/sorted()/filtered()’s own semantics. [docs]: the class docstring says in-place props["data"] updates have no effect; verified they work and preserve sort/filter/page/selection (reactable-py’s mounted component doesn’t memoize data).
render_panel
param(name) |
the parameter’s type |
seeded (the parameter’s current value) |
re-seeded, and re-fires even on an unchanged value (priority="event", so following a re-rendered object’s identity doesn’t get swallowed as a dedup) |
none |
param(name) is a dynamic accessor (the name isn’t known ahead of time): the rendered object must be a param.Parameterized (a panel widget, pane, or Viewer), or it raises TypeError; an unknown parameter name raises ValueError listing the object’s actual parameters.