Skip to content

Modules

RLBuilder

Bases: RouteLitBuilder

A builder for a RouteLit application. This Builder template serves as example on how to create a RouteLit custom components.

Source code in src/routelit_glide_data_grid/builder.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
class RLBuilder(RouteLitBuilder):
    """
    A builder for a RouteLit application.
    This Builder template serves as example on how to create a RouteLit custom components.
    """

    static_assets_targets: ClassVar[list[AssetTarget]] = [
        {
            "package_name": "routelit_glide_data_grid",
            "path": "static",
        }
    ]

    def _serialize_column_config(self, config: dict[str, ColumnConfig | str | None] | None) -> dict[str, Any] | None:
        if config is None:
            return None

        serialized = {}
        for col, cfg in config.items():
            if isinstance(cfg, ColumnConfig):
                # Include the class name as type
                data = cfg.to_dict()
                data["type"] = cfg.__class__.__name__
                serialized[col] = data
            else:
                serialized[col] = cfg
        return serialized

    def _prepare_grid_data(
        self,
        data: pd.DataFrame | dict | list,
        column_order: Iterable[str] | None,
    ) -> tuple[list[dict[str, Any]], list[str], dict[str, str]]:
        """Normalize data and extract/infer column information."""
        normalized = normalize_data(data)
        all_columns = extract_columns(normalized)

        # Filter and order columns if column_order is provided
        display_columns = [col for col in column_order if col in all_columns] if column_order else all_columns

        # Infer types for display columns
        column_types = infer_column_types(normalized, display_columns)

        return normalized, display_columns, column_types

    def _get_common_props(
        self,
        columns: list[str],
        column_types: dict[str, str],
        height: Literal["auto", "content", "stretch"] | int,
        width: Literal["stretch", "content"] | int,
        hide_index: bool | None,
        row_height: int | None,
        row_markers: str | None,
        freeze_columns: int,
        freeze_trailing_rows: int,
        trailing_row_options: dict[str, Any] | None,
        column_config: dict[str, ColumnConfig | str | None] | None,
        placeholder: str | None,
        theme: dict[str, Any] | None,
        element_id: str,
    ) -> dict[str, Any]:
        """Prepare props common to both data_grid and data_editor."""
        return {
            "columns": columns,
            "columnTypes": column_types,
            "height": height,
            "width": width,
            "hideIndex": hide_index,
            "rowHeight": row_height,
            "rowMarkers": row_markers,
            "freezeColumns": freeze_columns,
            "freezeTrailingRows": freeze_trailing_rows,
            "trailingRowOptions": trailing_row_options,
            "columnConfig": self._serialize_column_config(column_config),
            "placeholder": placeholder,
            "theme": theme,
            "id": element_id,
        }

    def data_grid(
        self,
        data: pd.DataFrame | dict | list,
        *,
        height: Literal["auto", "content", "stretch"] | int = "auto",
        width: Literal["stretch", "content"] | int = "stretch",
        hide_index: bool | None = None,
        row_height: int | None = None,
        row_markers: Literal["none", "number", "checkbox", "both"] | None = None,
        freeze_columns: int = 0,
        freeze_trailing_rows: int = 0,
        trailing_row_options: dict[str, Any] | None = None,
        selection_mode: Literal["single-row", "multi-row", "single-column", "multi-column", "single-cell", "multi-cell"]
        | Iterable[str]
        | None = None,
        on_select: Literal["ignore", "rerun"] | Callable[[dict], None] | None = "ignore",
        column_config: dict[str, ColumnConfig | str | None] | None = None,
        column_order: Iterable[str] | None = None,
        placeholder: str | None = None,
        search: str | None = None,
        theme: dict[str, Any] | None = None,
        key: str | None = None,
    ) -> GridSelection | None:
        """
        Display a read-only, interactive data grid with optional row/column selection.

        Args:
            data (Union[pd.DataFrame, dict, list]): The data to display. Supports
                pandas DataFrame, column-major dict, row-major list of dicts, simple
                lists, and key-value dicts.
            height (Union[Literal["auto", "content", "stretch"], int]): Grid
                height. "auto" (default), "content", "stretch", or pixel value.
            width (Union[Literal["stretch", "content"], int]): Grid width.
                "stretch" (default), "content", or pixel value.
            hide_index (Optional[bool]): Whether to hide the index column.
            row_height (Optional[int]): Fixed row height in pixels.
            row_markers (Optional[Literal["none", "number", "checkbox", "both"]]):
                Type of row markers to display.
            freeze_columns (int): Number of columns to freeze on the left.
            freeze_trailing_rows (int): Number of rows to freeze at the bottom.
            trailing_row_options (Optional[dict[str, Any]]): Options for the
                trailing row (hint, addIcon, etc.).
            selection_mode (Union[Literal["single-row", "multi-row", ...], Iterable[str], None]):
                Selection mode.
            on_select (Union[Literal["ignore", "rerun"], Callable[[dict], None], None]):
                Action on selection.
            column_config (Optional[dict[str, Union[ColumnConfig, str, None]]]):
                Dictionary of column names to ColumnConfig objects.
            column_order (Optional[Iterable[str]]): Ordered list of column names to display.
            placeholder (Optional[str]): Text to display for missing/None values.
            search (Optional[str]): Search query to filter data.
            theme (Optional[dict[str, Any]]): Theme override dictionary.
            key (Optional[str]): Stable widget identity.

        Returns:
            Optional[GridSelection]: The current selection state if on_select is
                not "ignore".
        """
        normalized, display_columns, column_types = self._prepare_grid_data(data, column_order)
        element_key = key or self._new_text_id("grid_data_grid")

        # Handle state
        current_selection = self.session_state.get(element_key)
        has_event, event_value = self._get_event_value(element_key, "select")
        if has_event:
            current_selection = event_value
            self.session_state[element_key] = current_selection
            if callable(on_select):
                on_select(current_selection)

        props = self._get_common_props(
            columns=display_columns,
            column_types=column_types,
            height=height,
            width=width,
            hide_index=hide_index,
            row_height=row_height,
            row_markers=row_markers,
            freeze_columns=freeze_columns,
            freeze_trailing_rows=freeze_trailing_rows,
            trailing_row_options=trailing_row_options,
            column_config=column_config,
            placeholder=placeholder,
            theme=theme,
            element_id=element_key,
        )
        props.update({
            "data": normalized,
            "selectionMode": selection_mode,
            "search": search,
            "selection": current_selection,
        })

        # Handle callbacks and return values
        if callable(on_select):
            props["onSelect"] = "callback"
        else:
            props["onSelect"] = on_select

        self._create_element(name="grid_data_grid", props=props, key=element_key)

        return current_selection

    @overload
    def data_editor(
        self,
        data: pd.DataFrame,
        *,
        height: Literal["auto", "content", "stretch"] | int = "auto",
        width: Literal["stretch", "content"] | int = "stretch",
        hide_index: bool | None = None,
        row_height: int | None = None,
        row_markers: Literal["none", "number", "checkbox", "both"] | None = None,
        freeze_columns: int = 0,
        freeze_trailing_rows: int = 0,
        trailing_row_options: dict[str, Any] | None = None,
        num_rows: Literal["fixed", "dynamic", "add", "delete"] = "fixed",
        disabled: bool | Iterable[str | int] = False,
        on_change: Callable[[pd.DataFrame], None] | None = None,
        column_config: dict[str, ColumnConfig | str | None] | None = None,
        column_order: Iterable[str] | None = None,
        placeholder: str | None = None,
        theme: dict[str, Any] | None = None,
        key: str | None = None,
    ) -> pd.DataFrame: ...

    @overload
    def data_editor(
        self,
        data: dict,
        *,
        height: Literal["auto", "content", "stretch"] | int = "auto",
        width: Literal["stretch", "content"] | int = "stretch",
        hide_index: bool | None = None,
        row_height: int | None = None,
        row_markers: Literal["none", "number", "checkbox", "both"] | None = None,
        freeze_columns: int = 0,
        freeze_trailing_rows: int = 0,
        trailing_row_options: dict[str, Any] | None = None,
        num_rows: Literal["fixed", "dynamic", "add", "delete"] = "fixed",
        disabled: bool | Iterable[str | int] = False,
        on_change: Callable[[dict], None] | None = None,
        column_config: dict[str, ColumnConfig | str | None] | None = None,
        column_order: Iterable[str] | None = None,
        placeholder: str | None = None,
        theme: dict[str, Any] | None = None,
        key: str | None = None,
    ) -> dict: ...

    @overload
    def data_editor(
        self,
        data: list,
        *,
        height: Literal["auto", "content", "stretch"] | int = "auto",
        width: Literal["stretch", "content"] | int = "stretch",
        hide_index: bool | None = None,
        row_height: int | None = None,
        row_markers: Literal["none", "number", "checkbox", "both"] | None = None,
        freeze_columns: int = 0,
        freeze_trailing_rows: int = 0,
        trailing_row_options: dict[str, Any] | None = None,
        num_rows: Literal["fixed", "dynamic", "add", "delete"] = "fixed",
        disabled: bool | Iterable[str | int] = False,
        on_change: Callable[[list], None] | None = None,
        column_config: dict[str, ColumnConfig | str | None] | None = None,
        column_order: Iterable[str] | None = None,
        placeholder: str | None = None,
        theme: dict[str, Any] | None = None,
        key: str | None = None,
    ) -> list: ...

    def data_editor(
        self,
        data: pd.DataFrame | dict | list,
        *,
        height: Literal["auto", "content", "stretch"] | int = "auto",
        width: Literal["stretch", "content"] | int = "stretch",
        hide_index: bool | None = None,
        row_height: int | None = None,
        row_markers: Literal["none", "number", "checkbox", "both"] | None = None,
        freeze_columns: int = 0,
        freeze_trailing_rows: int = 0,
        trailing_row_options: dict[str, Any] | None = None,
        num_rows: Literal["fixed", "dynamic", "add", "delete"] = "fixed",
        disabled: bool | Iterable[str | int] = False,
        on_change: Callable[[pd.DataFrame | dict | list], None] | None = None,
        column_config: dict[str, ColumnConfig | str | None] | None = None,
        column_order: Iterable[str] | None = None,
        placeholder: str | None = None,
        theme: dict[str, Any] | None = None,
        key: str | None = None,
    ) -> pd.DataFrame | dict | list:
        """
        Display an editable data grid with optional dynamic row management.

        Args:
            data (Union[pd.DataFrame, dict, list]): The initial data to display.
                Supports pandas DataFrame, column-major dict, row-major list of
                dicts, simple lists, and key-value dicts.
            height (Union[Literal["auto", "content", "stretch"], int]): Grid
                height. "auto" (default), "content", "stretch", or pixel value.
            width (Union[Literal["stretch", "content"], int]): Grid width.
                "stretch" (default), "content", or pixel value.
            hide_index (Optional[bool]): Whether to hide the index column.
            row_height (Optional[int]): Fixed row height in pixels.
            row_markers (Optional[Literal["none", "number", "checkbox", "both"]]):
                Type of row markers to display.
            freeze_columns (int): Number of columns to freeze on the left.
            freeze_trailing_rows (int): Number of rows to freeze at the bottom.
            trailing_row_options (Optional[dict[str, Any]]): Options for the
                trailing row (hint, addIcon, etc.).
            num_rows (Literal["fixed", "dynamic", "add", "delete"]): Row
                management mode.
            disabled (Union[bool, Iterable[Union[str, int]]]): Global disable
                flag, or list of column names/indices to disable.
            on_change (Optional[Callable]): Optional callback when data is modified.
            column_config (Optional[dict[str, Union[ColumnConfig, str, None]]]):
                Dictionary of column names to ColumnConfig objects.
            column_order (Optional[Iterable[str]]): Ordered list of column names to display.
            placeholder (Optional[str]): Text to display for missing/None values.
            theme (Optional[dict[str, Any]]): Theme override dictionary.
            key (Optional[str]): Stable widget identity.

        Returns:
            Union[pd.DataFrame, dict, list]: The current state of the data
                (updated after edits).
        """
        element_key = key or self._new_text_id("grid_data_editor")

        # Handle state
        current_data = self.session_state.get(element_key)
        if current_data is None:
            current_data = data
            self.session_state[element_key] = current_data

        has_event, event_value = self._get_event_value(element_key, "change", "data")
        if has_event:
            # event_value is the updated row-major list of dicts
            updated_data = event_value

            # Convert back to input type if needed
            if isinstance(data, pd.DataFrame):
                res = pd.DataFrame(updated_data)
            elif isinstance(data, dict):
                # Convert row-major list of dicts back to column-major dict
                res = {} if not updated_data else {k: [row[k] for row in updated_data] for k in updated_data[0]}
            else:
                res = updated_data

            current_data = res
            self.session_state[element_key] = current_data
            if on_change:
                on_change(current_data)

        normalized, display_columns, column_types = self._prepare_grid_data(current_data, column_order)

        props = self._get_common_props(
            columns=display_columns,
            column_types=column_types,
            height=height,
            width=width,
            hide_index=hide_index,
            row_height=row_height,
            row_markers=row_markers,
            freeze_columns=freeze_columns,
            freeze_trailing_rows=freeze_trailing_rows,
            trailing_row_options=trailing_row_options,
            column_config=column_config,
            placeholder=placeholder,
            theme=theme,
            element_id=element_key,
        )
        props.update({
            "data": normalized,
            "numRows": num_rows,
            "disabled": disabled,
        })

        # Handle on_change callback
        if on_change:
            props["onChange"] = "callback"
        else:
            # Default to rerun for data_editor so it returns edited data
            props["onChange"] = "rerun"

        self._create_element(name="grid_data_editor", props=props, key=element_key)

        return current_data

data_editor(data, *, height='auto', width='stretch', hide_index=None, row_height=None, row_markers=None, freeze_columns=0, freeze_trailing_rows=0, trailing_row_options=None, num_rows='fixed', disabled=False, on_change=None, column_config=None, column_order=None, placeholder=None, theme=None, key=None)

data_editor(
    data: pd.DataFrame,
    *,
    height: Literal["auto", "content", "stretch"]
    | int = "auto",
    width: Literal["stretch", "content"] | int = "stretch",
    hide_index: bool | None = None,
    row_height: int | None = None,
    row_markers: Literal[
        "none", "number", "checkbox", "both"
    ]
    | None = None,
    freeze_columns: int = 0,
    freeze_trailing_rows: int = 0,
    trailing_row_options: dict[str, Any] | None = None,
    num_rows: Literal[
        "fixed", "dynamic", "add", "delete"
    ] = "fixed",
    disabled: bool | Iterable[str | int] = False,
    on_change: Callable[[pd.DataFrame], None] | None = None,
    column_config: dict[str, ColumnConfig | str | None]
    | None = None,
    column_order: Iterable[str] | None = None,
    placeholder: str | None = None,
    theme: dict[str, Any] | None = None,
    key: str | None = None,
) -> pd.DataFrame
data_editor(
    data: dict,
    *,
    height: Literal["auto", "content", "stretch"]
    | int = "auto",
    width: Literal["stretch", "content"] | int = "stretch",
    hide_index: bool | None = None,
    row_height: int | None = None,
    row_markers: Literal[
        "none", "number", "checkbox", "both"
    ]
    | None = None,
    freeze_columns: int = 0,
    freeze_trailing_rows: int = 0,
    trailing_row_options: dict[str, Any] | None = None,
    num_rows: Literal[
        "fixed", "dynamic", "add", "delete"
    ] = "fixed",
    disabled: bool | Iterable[str | int] = False,
    on_change: Callable[[dict], None] | None = None,
    column_config: dict[str, ColumnConfig | str | None]
    | None = None,
    column_order: Iterable[str] | None = None,
    placeholder: str | None = None,
    theme: dict[str, Any] | None = None,
    key: str | None = None,
) -> dict
data_editor(
    data: list,
    *,
    height: Literal["auto", "content", "stretch"]
    | int = "auto",
    width: Literal["stretch", "content"] | int = "stretch",
    hide_index: bool | None = None,
    row_height: int | None = None,
    row_markers: Literal[
        "none", "number", "checkbox", "both"
    ]
    | None = None,
    freeze_columns: int = 0,
    freeze_trailing_rows: int = 0,
    trailing_row_options: dict[str, Any] | None = None,
    num_rows: Literal[
        "fixed", "dynamic", "add", "delete"
    ] = "fixed",
    disabled: bool | Iterable[str | int] = False,
    on_change: Callable[[list], None] | None = None,
    column_config: dict[str, ColumnConfig | str | None]
    | None = None,
    column_order: Iterable[str] | None = None,
    placeholder: str | None = None,
    theme: dict[str, Any] | None = None,
    key: str | None = None,
) -> list

Display an editable data grid with optional dynamic row management.

Parameters:

Name Type Description Default
data Union[DataFrame, dict, list]

The initial data to display. Supports pandas DataFrame, column-major dict, row-major list of dicts, simple lists, and key-value dicts.

required
height Union[Literal['auto', 'content', 'stretch'], int]

Grid height. "auto" (default), "content", "stretch", or pixel value.

'auto'
width Union[Literal['stretch', 'content'], int]

Grid width. "stretch" (default), "content", or pixel value.

'stretch'
hide_index Optional[bool]

Whether to hide the index column.

None
row_height Optional[int]

Fixed row height in pixels.

None
row_markers Optional[Literal['none', 'number', 'checkbox', 'both']]

Type of row markers to display.

None
freeze_columns int

Number of columns to freeze on the left.

0
freeze_trailing_rows int

Number of rows to freeze at the bottom.

0
trailing_row_options Optional[dict[str, Any]]

Options for the trailing row (hint, addIcon, etc.).

None
num_rows Literal['fixed', 'dynamic', 'add', 'delete']

Row management mode.

'fixed'
disabled Union[bool, Iterable[Union[str, int]]]

Global disable flag, or list of column names/indices to disable.

False
on_change Optional[Callable]

Optional callback when data is modified.

None
column_config Optional[dict[str, Union[ColumnConfig, str, None]]]

Dictionary of column names to ColumnConfig objects.

None
column_order Optional[Iterable[str]]

Ordered list of column names to display.

None
placeholder Optional[str]

Text to display for missing/None values.

None
theme Optional[dict[str, Any]]

Theme override dictionary.

None
key Optional[str]

Stable widget identity.

None

Returns:

Type Description
DataFrame | dict | list

Union[pd.DataFrame, dict, list]: The current state of the data (updated after edits).

Source code in src/routelit_glide_data_grid/builder.py
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
def data_editor(
    self,
    data: pd.DataFrame | dict | list,
    *,
    height: Literal["auto", "content", "stretch"] | int = "auto",
    width: Literal["stretch", "content"] | int = "stretch",
    hide_index: bool | None = None,
    row_height: int | None = None,
    row_markers: Literal["none", "number", "checkbox", "both"] | None = None,
    freeze_columns: int = 0,
    freeze_trailing_rows: int = 0,
    trailing_row_options: dict[str, Any] | None = None,
    num_rows: Literal["fixed", "dynamic", "add", "delete"] = "fixed",
    disabled: bool | Iterable[str | int] = False,
    on_change: Callable[[pd.DataFrame | dict | list], None] | None = None,
    column_config: dict[str, ColumnConfig | str | None] | None = None,
    column_order: Iterable[str] | None = None,
    placeholder: str | None = None,
    theme: dict[str, Any] | None = None,
    key: str | None = None,
) -> pd.DataFrame | dict | list:
    """
    Display an editable data grid with optional dynamic row management.

    Args:
        data (Union[pd.DataFrame, dict, list]): The initial data to display.
            Supports pandas DataFrame, column-major dict, row-major list of
            dicts, simple lists, and key-value dicts.
        height (Union[Literal["auto", "content", "stretch"], int]): Grid
            height. "auto" (default), "content", "stretch", or pixel value.
        width (Union[Literal["stretch", "content"], int]): Grid width.
            "stretch" (default), "content", or pixel value.
        hide_index (Optional[bool]): Whether to hide the index column.
        row_height (Optional[int]): Fixed row height in pixels.
        row_markers (Optional[Literal["none", "number", "checkbox", "both"]]):
            Type of row markers to display.
        freeze_columns (int): Number of columns to freeze on the left.
        freeze_trailing_rows (int): Number of rows to freeze at the bottom.
        trailing_row_options (Optional[dict[str, Any]]): Options for the
            trailing row (hint, addIcon, etc.).
        num_rows (Literal["fixed", "dynamic", "add", "delete"]): Row
            management mode.
        disabled (Union[bool, Iterable[Union[str, int]]]): Global disable
            flag, or list of column names/indices to disable.
        on_change (Optional[Callable]): Optional callback when data is modified.
        column_config (Optional[dict[str, Union[ColumnConfig, str, None]]]):
            Dictionary of column names to ColumnConfig objects.
        column_order (Optional[Iterable[str]]): Ordered list of column names to display.
        placeholder (Optional[str]): Text to display for missing/None values.
        theme (Optional[dict[str, Any]]): Theme override dictionary.
        key (Optional[str]): Stable widget identity.

    Returns:
        Union[pd.DataFrame, dict, list]: The current state of the data
            (updated after edits).
    """
    element_key = key or self._new_text_id("grid_data_editor")

    # Handle state
    current_data = self.session_state.get(element_key)
    if current_data is None:
        current_data = data
        self.session_state[element_key] = current_data

    has_event, event_value = self._get_event_value(element_key, "change", "data")
    if has_event:
        # event_value is the updated row-major list of dicts
        updated_data = event_value

        # Convert back to input type if needed
        if isinstance(data, pd.DataFrame):
            res = pd.DataFrame(updated_data)
        elif isinstance(data, dict):
            # Convert row-major list of dicts back to column-major dict
            res = {} if not updated_data else {k: [row[k] for row in updated_data] for k in updated_data[0]}
        else:
            res = updated_data

        current_data = res
        self.session_state[element_key] = current_data
        if on_change:
            on_change(current_data)

    normalized, display_columns, column_types = self._prepare_grid_data(current_data, column_order)

    props = self._get_common_props(
        columns=display_columns,
        column_types=column_types,
        height=height,
        width=width,
        hide_index=hide_index,
        row_height=row_height,
        row_markers=row_markers,
        freeze_columns=freeze_columns,
        freeze_trailing_rows=freeze_trailing_rows,
        trailing_row_options=trailing_row_options,
        column_config=column_config,
        placeholder=placeholder,
        theme=theme,
        element_id=element_key,
    )
    props.update({
        "data": normalized,
        "numRows": num_rows,
        "disabled": disabled,
    })

    # Handle on_change callback
    if on_change:
        props["onChange"] = "callback"
    else:
        # Default to rerun for data_editor so it returns edited data
        props["onChange"] = "rerun"

    self._create_element(name="grid_data_editor", props=props, key=element_key)

    return current_data

data_grid(data, *, height='auto', width='stretch', hide_index=None, row_height=None, row_markers=None, freeze_columns=0, freeze_trailing_rows=0, trailing_row_options=None, selection_mode=None, on_select='ignore', column_config=None, column_order=None, placeholder=None, search=None, theme=None, key=None)

Display a read-only, interactive data grid with optional row/column selection.

Parameters:

Name Type Description Default
data Union[DataFrame, dict, list]

The data to display. Supports pandas DataFrame, column-major dict, row-major list of dicts, simple lists, and key-value dicts.

required
height Union[Literal['auto', 'content', 'stretch'], int]

Grid height. "auto" (default), "content", "stretch", or pixel value.

'auto'
width Union[Literal['stretch', 'content'], int]

Grid width. "stretch" (default), "content", or pixel value.

'stretch'
hide_index Optional[bool]

Whether to hide the index column.

None
row_height Optional[int]

Fixed row height in pixels.

None
row_markers Optional[Literal['none', 'number', 'checkbox', 'both']]

Type of row markers to display.

None
freeze_columns int

Number of columns to freeze on the left.

0
freeze_trailing_rows int

Number of rows to freeze at the bottom.

0
trailing_row_options Optional[dict[str, Any]]

Options for the trailing row (hint, addIcon, etc.).

None
selection_mode Union[Literal['single-row', 'multi-row', ...], Iterable[str], None]

Selection mode.

None
on_select Union[Literal['ignore', 'rerun'], Callable[[dict], None], None]

Action on selection.

'ignore'
column_config Optional[dict[str, Union[ColumnConfig, str, None]]]

Dictionary of column names to ColumnConfig objects.

None
column_order Optional[Iterable[str]]

Ordered list of column names to display.

None
placeholder Optional[str]

Text to display for missing/None values.

None
search Optional[str]

Search query to filter data.

None
theme Optional[dict[str, Any]]

Theme override dictionary.

None
key Optional[str]

Stable widget identity.

None

Returns:

Type Description
GridSelection | None

Optional[GridSelection]: The current selection state if on_select is not "ignore".

Source code in src/routelit_glide_data_grid/builder.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
def data_grid(
    self,
    data: pd.DataFrame | dict | list,
    *,
    height: Literal["auto", "content", "stretch"] | int = "auto",
    width: Literal["stretch", "content"] | int = "stretch",
    hide_index: bool | None = None,
    row_height: int | None = None,
    row_markers: Literal["none", "number", "checkbox", "both"] | None = None,
    freeze_columns: int = 0,
    freeze_trailing_rows: int = 0,
    trailing_row_options: dict[str, Any] | None = None,
    selection_mode: Literal["single-row", "multi-row", "single-column", "multi-column", "single-cell", "multi-cell"]
    | Iterable[str]
    | None = None,
    on_select: Literal["ignore", "rerun"] | Callable[[dict], None] | None = "ignore",
    column_config: dict[str, ColumnConfig | str | None] | None = None,
    column_order: Iterable[str] | None = None,
    placeholder: str | None = None,
    search: str | None = None,
    theme: dict[str, Any] | None = None,
    key: str | None = None,
) -> GridSelection | None:
    """
    Display a read-only, interactive data grid with optional row/column selection.

    Args:
        data (Union[pd.DataFrame, dict, list]): The data to display. Supports
            pandas DataFrame, column-major dict, row-major list of dicts, simple
            lists, and key-value dicts.
        height (Union[Literal["auto", "content", "stretch"], int]): Grid
            height. "auto" (default), "content", "stretch", or pixel value.
        width (Union[Literal["stretch", "content"], int]): Grid width.
            "stretch" (default), "content", or pixel value.
        hide_index (Optional[bool]): Whether to hide the index column.
        row_height (Optional[int]): Fixed row height in pixels.
        row_markers (Optional[Literal["none", "number", "checkbox", "both"]]):
            Type of row markers to display.
        freeze_columns (int): Number of columns to freeze on the left.
        freeze_trailing_rows (int): Number of rows to freeze at the bottom.
        trailing_row_options (Optional[dict[str, Any]]): Options for the
            trailing row (hint, addIcon, etc.).
        selection_mode (Union[Literal["single-row", "multi-row", ...], Iterable[str], None]):
            Selection mode.
        on_select (Union[Literal["ignore", "rerun"], Callable[[dict], None], None]):
            Action on selection.
        column_config (Optional[dict[str, Union[ColumnConfig, str, None]]]):
            Dictionary of column names to ColumnConfig objects.
        column_order (Optional[Iterable[str]]): Ordered list of column names to display.
        placeholder (Optional[str]): Text to display for missing/None values.
        search (Optional[str]): Search query to filter data.
        theme (Optional[dict[str, Any]]): Theme override dictionary.
        key (Optional[str]): Stable widget identity.

    Returns:
        Optional[GridSelection]: The current selection state if on_select is
            not "ignore".
    """
    normalized, display_columns, column_types = self._prepare_grid_data(data, column_order)
    element_key = key or self._new_text_id("grid_data_grid")

    # Handle state
    current_selection = self.session_state.get(element_key)
    has_event, event_value = self._get_event_value(element_key, "select")
    if has_event:
        current_selection = event_value
        self.session_state[element_key] = current_selection
        if callable(on_select):
            on_select(current_selection)

    props = self._get_common_props(
        columns=display_columns,
        column_types=column_types,
        height=height,
        width=width,
        hide_index=hide_index,
        row_height=row_height,
        row_markers=row_markers,
        freeze_columns=freeze_columns,
        freeze_trailing_rows=freeze_trailing_rows,
        trailing_row_options=trailing_row_options,
        column_config=column_config,
        placeholder=placeholder,
        theme=theme,
        element_id=element_key,
    )
    props.update({
        "data": normalized,
        "selectionMode": selection_mode,
        "search": search,
        "selection": current_selection,
    })

    # Handle callbacks and return values
    if callable(on_select):
        props["onSelect"] = "callback"
    else:
        props["onSelect"] = on_select

    self._create_element(name="grid_data_grid", props=props, key=element_key)

    return current_selection