render_reactable

render_reactable class

Reactively render a reactable table (the Python port of R’s reactable).

Usage Source

render_reactable(
    _fn: Optional[ValueFn[ValueT]] = None,
    *,
    width: Optional[str] = None,
    height: Optional[str] = None,
    fill: Optional[bool] = None,
    fillable: Optional[bool] = None,
    rate_policy: Optional[RatePolicy] = None,
    rate_delay: Optional[float] = None,
)

reactable builds interactive data tables – sorting, per-column and global filtering, searching, pagination, row selection, row grouping/aggregation, expandable detail rows, custom cell renderers (Python or JavaScript), and themes – all computed client-side, so interacting with the table never round-trips to the server.

@render_reactable is a decorator that creates a render function – a function that returns the table to display and re-runs when the reactive values it reads change:

from reactable import Reactable
from reactable.data import cars_93

from shinywidgets import render_reactable


@render_reactable
def tbl():
    return Reactable(cars_93)

The render function may return either a reactable.Reactable (the usual choice; it is coerced to its widget) or a reactable.widgets.ReactableWidget. Both require pip install 'shinywidgets[reactable]'.

Note

Accessing the rendered widget: the render function gains two read-only attributes: .value, the value it most recently returned, and .widget, the rendered ReactableWidget instance.

Responding to user interaction: read the output’s accessors in a reactive context, e.g. tbl.selected() or tbl.sorted() (see the methods below). Each silently cancels (like req(False)) until the browser has reported the table’s state, shortly after the table first renders.

Row clicks aren’t part of the reported state, but reactable’s own on_click=JS(...) prop can forward them as an ordinary Shiny input:

Reactable(
    df,
    on_click=JS(
        "function(rowInfo, colInfo, state) {"
        "  Shiny.setInputValue('row_clicked', {row: rowInfo.index, col: colInfo.id}, {priority: 'event'});"
        "}"
    ),
)

Mutating the widget: to update the table when the data changes, either re-render it (i.e., have the render function read the relevant inputs/reactives) or send new data through .widget.props – a props update preserves the user’s sort, filter, page, and selection. Note the dict is a trait, so replace it (mutating .widget.props["data"] in place never syncs), for example:

@reactive.effect
def _():
    tbl.widget.props = {**tbl.widget.props, "data": df().to_dict(orient="records")}

Other props update the same way:

@reactive.effect
def _():
    tbl.widget.props = {**tbl.widget.props, "defaultPageSize": input.page_size()}

The accessors address the table through its element_id prop, so render_reactable defaults it to a unique value when unset (a user-provided element_id is respected, and must be unique per page, as in R).

In a filling layout, the table fills its container. Setting reactable’s own height= prop opts out of filling (akin to an explicit ipywidgets Layout(height=...)). Missing values (NaN) in the data render as empty cells.

Parameters

width : Optional[str] = None

A CSS length unit (e.g., "400px" or "100%") for the output’s width.

height : Optional[str] = None

A CSS length unit for the output’s height. When None (the default), the output is designed to fill the height of its parent container (see fill and fillable).

fill : Optional[bool] = None

Whether the output should grow or shrink to fit its parent container. When None (the default), this is True if height is not specified.

fillable : Optional[bool] = None

Whether the output container should fill available space (scrolling its contents on overflow). When None (the default), this is True if height is not specified.

Examples

from reactable import ColFormat, Column, Reactable
from reactable.data import cars_93
from shiny.express import ui
from shinywidgets import render_reactable


@render_reactable
def tbl():
    return Reactable(
        cars_93[:, ["manufacturer", "model", "price"]],
        columns={"price": Column(format=ColFormat(prefix="$"))},
        filterable=True,
        searchable=True,
    )

Each accessor can also be read by name – out.read("selected") – which also accepts the widget’s bare synced-trait names.

See also

output_widget(): in Shiny Core, the UI needs this to hold the output (Shiny Express displays it where the render function is defined).

Methods

filtered() The current per-column filters, as a list of {"id": column, "value": filter} entries (empty when unfiltered).
page() The current page, 0-based (reactable’s own convention; add 1 to match the page numbers the table displays).
search() The current global search string ("" when the search box is empty).
selected() The currently selected rows, as 0-based positions in the table’s data.
sorted() The current sort, as a list of {"id": column, "desc": bool} entries (one per sorted column, in priority order; empty when unsorted).

filtered()

render_reactable.filtered()

The current per-column filters, as a list of {"id": column, "value": filter} entries (empty when unfiltered).

Silently cancels (req(False)) until the browser first reports the table’s state, shortly after render. Debounced (250 ms) by default, since filters update per keystroke; see the rate_policy and rate_delay parameters.

page()

render_reactable.page()

The current page, 0-based (reactable’s own convention; add 1 to match the page numbers the table displays).

Silently cancels (req(False)) until the browser first reports the table’s state, shortly after render. Not rate-limited.

search()

render_reactable.search()

The current global search string ("" when the search box is empty).

Silently cancels (req(False)) until the browser first reports the table’s state, shortly after render. Debounced (250 ms) by default, since search updates per keystroke; see the rate_policy and rate_delay parameters.

selected()

render_reactable.selected()

The currently selected rows, as 0-based positions in the table’s data.

Positions are in data coordinates (like df.iloc), not display coordinates: sorting, filtering, or paging the table doesn’t change them. Empty when nothing is selected. Requires the table’s selection= prop to be set. Silently cancels (req(False)) until the browser first reports the table’s state, shortly after render. Not rate-limited.

Positions are into the current data: after a re-render with new data (or a .widget.props data replacement), a retained position can point at a different row until the table re-reports. To track rows across data changes, include an ID column and resolve positions against the data the table currently shows (df.iloc[tbl.selected()]["id"]).

sorted()

render_reactable.sorted()

The current sort, as a list of {"id": column, "desc": bool} entries (one per sorted column, in priority order; empty when unsorted).

Silently cancels (req(False)) until the browser first reports the table’s state, shortly after render. Not rate-limited.