> For AI agents: the complete documentation index is at [llms.txt](https://reflex.dev/docs/llms.txt). Markdown versions are available by appending `.md` or sending `Accept: text/markdown`.

---
components:
  - rx.data_editor
---

# Data Editor

A datagrid editor based on [Glide Data Grid](https://grid.glideapps.com/)

```python exec
import reflex as rx
from typing import Any

columns: list[dict[str, str]] = [
    {
        "title": "Code",
        "type": "str",
    },
    {
        "title": "Value",
        "type": "int",
    },
    {
        "title": "Activated",
        "type": "bool",
    },
]
data: list[list[Any]] = [
    ["A", 1, True],
    ["B", 2, False],
    ["C", 3, False],
    ["D", 4, True],
    ["E", 5, True],
    ["F", 6, False],
]
```

This component is introduced as an alternative to the [datatable](/docs/library/tables-and-data-grids/data-table) to support editing the displayed data.

## Columns

The columns definition should be a `list` of `dict`, each `dict` describing the associated columns.
Property of a column dict:

- `title`: The text to display in the header of the column.
- `id`: An id for the column, if not defined, will default to a lower case of `title`
- `width`: The width of the column.
- `type`: The type of the columns, default to `"str"`.

## Data

The `data` props of `rx.data_editor` accept a `list` of `list`, where each `list` represent a row of data to display in the table.

## Simple Example

Here is a basic example of using the data_editor representing data with no interaction and no styling. Below we define the `columns` and the `data` which are taken in by the `rx.data_editor` component. When we define the `columns` we must define a `title` and a `type` for each column we create. The columns in the `data` must then match the defined `type` or errors will be thrown.

```python demo box
rx.data_editor(
    columns=columns,
    data=data,
)
```

```python
columns: list[dict[str, str]] = [
    {
        "title": "Code",
        "type": "str",
    },
    {
        "title": "Value",
        "type": "int",
    },
    {
        "title": "Activated",
        "type": "bool",
    },
]
data: list[list[Any]] = [
    ["A", 1, True],
    ["B", 2, False],
    ["C", 3, False],
    ["D", 4, True],
    ["E", 5, True],
    ["F", 6, False],
]
```

```python
rx.data_editor(
    columns=columns,
    data=data,
)
```

## Interactive Example

```python exec
class DataEditorState_HP(rx.State):
    clicked_data: str = "Cell clicked: "
    cols: list[Any] = [
        {"title": "Title", "type": "str"},
        {
            "title": "Name",
            "type": "str",
            "group": "Data",
            "width": 300,
        },
        {
            "title": "Birth",
            "type": "str",
            "id": "date",
            "group": "Data",
            "width": 150,
        },
        {
            "title": "Human",
            "type": "bool",
            "group": "Data",
            "width": 80,
        },
        {
            "title": "House",
            "type": "str",
            "id": "date",
            "group": "Data",
        },
        {
            "title": "Wand",
            "type": "str",
            "id": "date",
            "group": "Data",
            "width": 250,
        },
        {
            "title": "Patronus",
            "type": "str",
            "id": "date",
            "group": "Data",
        },
        {
            "title": "Blood status",
            "type": "str",
            "id": "date",
            "group": "Data",
            "width": 200,
        },
    ]

    data = [
        [
            "1",
            "Harry James Potter",
            "31 July 1980",
            True,
            "Gryffindor",
            "11'  Holly  phoenix feather",
            "Stag",
            "Half-blood",
        ],
        [
            "2",
            "Ronald Bilius Weasley",
            "1 March 1980",
            True,
            "Gryffindor",
            "12' Ash unicorn tail hair",
            "Jack Russell terrier",
            "Pure-blood",
        ],
        [
            "3",
            "Hermione Jean Granger",
            "19 September, 1979",
            True,
            "Gryffindor",
            "10¾'  vine wood dragon heartstring",
            "Otter",
            "Muggle-born",
        ],
        [
            "4",
            "Albus Percival Wulfric Brian Dumbledore",
            "Late August 1881",
            True,
            "Gryffindor",
            "15' Elder Thestral tail hair core",
            "Phoenix",
            "Half-blood",
        ],
        [
            "5",
            "Rubeus Hagrid",
            "6 December 1928",
            False,
            "Gryffindor",
            "16'  Oak unknown core",
            "None",
            "Part-Human (Half-giant)",
        ],
        [
            "6",
            "Fred Weasley",
            "1 April, 1978",
            True,
            "Gryffindor",
            "Unknown",
            "Unknown",
            "Pure-blood",
        ],
    ]

    def click_cell(self, pos):
        col, row = pos
        yield self.get_clicked_data(pos)

    def get_clicked_data(self, pos) -> str:
        self.clicked_data = f"Cell clicked: {pos}"
```

Here we define a State, as shown below, that allows us to print the location of the cell as a heading when we click on it, using the `on_cell_clicked` `event trigger`. Check out all the other `event triggers` that you can use with datatable at the bottom of this page. We also define a `group` with a label `Data`. This groups all the columns with this `group` label under a larger group `Data` as seen in the table below.

```python demo box
rx.heading(DataEditorState_HP.clicked_data)
```

```python demo box
rx.data_editor(
    columns=DataEditorState_HP.cols,
    data=DataEditorState_HP.data,
    on_cell_clicked=DataEditorState_HP.click_cell,
)
```

```python
class DataEditorState_HP(rx.State):
    clicked_data: str = "Cell clicked: "

    cols: list[Any] = [
        {"title": "Title", "type": "str"},
        {
            "title": "Name",
            "type": "str",
            "group": "Data",
            "width": 300,
        },
        {
            "title": "Birth",
            "type": "str",
            "group": "Data",
            "width": 150,
        },
        {
            "title": "Human",
            "type": "bool",
            "group": "Data",
            "width": 80,
        },
        {
            "title": "House",
            "type": "str",
            "group": "Data",
        },
        {
            "title": "Wand",
            "type": "str",
            "group": "Data",
            "width": 250,
        },
        {
            "title": "Patronus",
            "type": "str",
            "group": "Data",
        },
        {
            "title": "Blood status",
            "type": "str",
            "group": "Data",
            "width": 200,
        },
    ]

    data = [
        [
            "1",
            "Harry James Potter",
            "31 July 1980",
            True,
            "Gryffindor",
            "11'  Holly  phoenix feather",
            "Stag",
            "Half-blood",
        ],
        [
            "2",
            "Ronald Bilius Weasley",
            "1 March 1980",
            True,
            "Gryffindor",
            "12' Ash unicorn tail hair",
            "Jack Russell terrier",
            "Pure-blood",
        ],
        [
            "3",
            "Hermione Jean Granger",
            "19 September, 1979",
            True,
            "Gryffindor",
            "10¾'  vine wood dragon heartstring",
            "Otter",
            "Muggle-born",
        ],
        [
            "4",
            "Albus Percival Wulfric Brian Dumbledore",
            "Late August 1881",
            True,
            "Gryffindor",
            "15' Elder Thestral tail hair core",
            "Phoenix",
            "Half-blood",
        ],
        [
            "5",
            "Rubeus Hagrid",
            "6 December 1928",
            False,
            "Gryffindor",
            "16'  Oak unknown core",
            "None",
            "Part-Human (Half-giant)",
        ],
        [
            "6",
            "Fred Weasley",
            "1 April, 1978",
            True,
            "Gryffindor",
            "Unknown",
            "Unknown",
            "Pure-blood",
        ],
    ]

    def click_cell(self, pos):
        col, row = pos
        yield self.get_clicked_data(pos)

    def get_clicked_data(self, pos) -> str:
        self.clicked_data = f"Cell clicked: {pos}"
```

```python
rx.data_editor(
    columns=DataEditorState_HP.cols,
    data=DataEditorState_HP.data,
    on_cell_clicked=DataEditorState_HP.click_cell,
)
```

## Styling Example

Now let's style our datatable to make it look more aesthetic and easier to use. We must first import `DataEditorTheme` and then we can start setting our style props as seen below in `dark_theme`.

We then set these themes using `theme=DataEditorTheme(**dark_theme)`. On top of the styling we can also set some `props` to make some other aesthetic changes to our datatable. We have set the `row_height` to equal `50` so that the content is easier to read. We have also made the `smooth_scroll_x` and `smooth_scroll_y` equal `True` so that we can smoothly scroll along the columns and rows. Finally, we added `column_select=single`, where column select can take any of the following values `none`, `single` or `multiple`.

```python exec
from reflex.components.datadisplay.dataeditor import DataEditorTheme

dark_theme = {
    "accentColor": "#8c96ff",
    "accentLight": "rgba(202, 206, 255, 0.253)",
    "textDark": "#ffffff",
    "textMedium": "#b8b8b8",
    "textLight": "#a0a0a0",
    "textBubble": "#ffffff",
    "bgIconHeader": "#b8b8b8",
    "fgIconHeader": "#000000",
    "textHeader": "#a1a1a1",
    "textHeaderSelected": "#000000",
    "bgCell": "#16161b",
    "bgCellMedium": "#202027",
    "bgHeader": "#212121",
    "bgHeaderHasFocus": "#474747",
    "bgHeaderHovered": "#404040",
    "bgBubble": "#212121",
    "bgBubbleSelected": "#000000",
    "bgSearchResult": "#423c24",
    "borderColor": "rgba(225,225,225,0.2)",
    "drilldownBorder": "rgba(225,225,225,0.4)",
    "linkColor": "#4F5DFF",
    "headerFontStyle": "bold 14px",
    "baseFontStyle": "13px",
    "fontFamily": "Inter, Roboto, -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Ubuntu, noto, arial, sans-serif",
}
```

```python demo box
rx.data_editor(
    columns=DataEditorState_HP.cols,
    data=DataEditorState_HP.data,
    row_height=80,
    smooth_scroll_x=True,
    smooth_scroll_y=True,
    column_select="single",
    theme=DataEditorTheme(**dark_theme),
    height="30vh",
)
```

```python
from reflex.components.datadisplay.dataeditor import DataEditorTheme

dark_theme_snake_case = {
    "accent_color": "#8c96ff",
    "accent_light": "rgba(202, 206, 255, 0.253)",
    "text_dark": "#ffffff",
    "text_medium": "#b8b8b8",
    "text_light": "#a0a0a0",
    "text_bubble": "#ffffff",
    "bg_icon_header": "#b8b8b8",
    "fg_icon_header": "#000000",
    "text_header": "#a1a1a1",
    "text_header_selected": "#000000",
    "bg_cell": "#16161b",
    "bg_cell_medium": "#202027",
    "bg_header": "#212121",
    "bg_header_has_focus": "#474747",
    "bg_header_hovered": "#404040",
    "bg_bubble": "#212121",
    "bg_bubble_selected": "#000000",
    "bg_search_result": "#423c24",
    "border_color": "rgba(225,225,225,0.2)",
    "drilldown_border": "rgba(225,225,225,0.4)",
    "link_color": "#4F5DFF",
    "header_font_style": "bold 14px",
    "base_font_style": "13px",
    "font_family": "Inter, Roboto, -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Ubuntu, noto, arial, sans-serif",
}
```

```python
rx.data_editor(
    columns=DataEditorState_HP.cols,
    data=DataEditorState_HP.data,
    row_height=80,
    smooth_scroll_x=True,
    smooth_scroll_y=True,
    column_select="single",
    theme=DataEditorTheme(**dark_theme),
    height="30vh",
)
```

## API Reference

### rx.data_editor

The DataEditor Component.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `rows` | int | - | Number of rows. |
| `columns` | Sequence[dict[str, Any]] | - | Headers of the columns for the data grid. |
| `data` | Sequence[Sequence[Any]] | - | The data. |
| `get_cell_content` | str | - | The name of the callback used to find the data to display. |
| `get_cells_for_selection` | bool | - | Allow selection for copying. |
| `draw_focus_ring` | bool | - | Controls the drawing of the focus ring. |
| `fixed_shadow_x` | bool | - | Enables or disables the overlay shadow when scrolling horizontally. |
| `fixed_shadow_y` | bool | - | Enables or disables the overlay shadow when scrolling vertically. |
| `fill_handle` | bool | - | Controls the presence of the fill indicator. |
| `freeze_columns` | int | - | The number of columns which should remain in place when scrolling horizontally. Doesn't include rowMarkers. |
| `group_header_height` | int | - | Controls the header of the group header row. |
| `header_height` | int | - | Controls the height of the header row. |
| `max_column_auto_width` | int | - | The maximum width a column can be automatically sized to. |
| `max_column_width` | int | - | The maximum width a column can be resized to. |
| `min_column_width` | int | - | The minimum width a column can be resized to. |
| `row_height` | int | - | Determines the height of each row. |
| `row_markers` | Literal["none", "number", "checkbox", "both", "checkbox-visible", "clickable-number"] | - | Kind of row markers. Options are: "none", "number", "checkbox", "both", "checkbox-visible", "clickable-number". |
| `row_marker_start_index` | int | - | Changes the starting index for row markers. |
| `row_marker_width` | int | - | Sets the width of row markers in pixels, if unset row markers will automatically size. |
| `smooth_scroll_x` | bool | - | Enable horizontal smooth scrolling. |
| `smooth_scroll_y` | bool | - | Enable vertical smooth scrolling. |
| `vertical_border` | bool | - | Controls the drawing of the left hand vertical border of a column. If set to a boolean value it controls all borders. |
| `column_select` | Literal["none", "single", "multi"] | - | Allow columns selections. ("none", "single", "multi"). |
| `range_select` | Literal["none", "cell", "rect", "multi-cell", "multi-rect"] | - | Allow range selections. ("none", "cell", "rect", "multi-cell", "multi-rect"). |
| `row_select` | Literal["none", "single", "multi"] | - | Allow row selections. ("none", "single", "multi"). |
| `prevent_diagonal_scrolling` | bool | - | Prevent diagonal scrolling. |
| `overscroll_x` | int | - | Allow to scroll past the limit of the actual content on the horizontal axis. |
| `overscroll_y` | int | - | Allow to scroll past the limit of the actual content on the vertical axis. |
| `scroll_offset_x` | int | - | Initial scroll offset on the horizontal axis. |
| `scroll_offset_y` | int | - | Initial scroll offset on the vertical axis. |
| `range_selection_blending` | Literal["exclusive", "mixed"] | - | Controls which types of range selections can exist at the same time. ("exclusive", "mixed"). |
| `column_selection_blending` | Literal["exclusive", "mixed"] | - | Controls which types of column selections can exist at the same time. ("exclusive", "mixed"). |
| `row_selection_blending` | Literal["exclusive", "mixed"] | - | Controls which types of row selections can exist at the same time. ("exclusive", "mixed"). |
| `row_selection_mode` | Literal["auto", "multi"] | - | Controls row marker selection behavior. "auto" adapts to touch/mouse, "multi" acts as if Ctrl is pressed. ("auto", "multi"). |
| `span_range_behavior` | Literal["default", "allowPartial"] | - | Controls how spans are handled in selections. ("default", "allowPartial"). |
| `theme` | DataEditorTheme, dict | - | global theme. |
| `grid_selection` | GridSelection | - | The current grid selection state (columns, rows, and current cell/range). Must be used when on_grid_selection_change is used otherwise updates will not be reflected in the grid. |
| `show_search` | bool | - | Shows search bar. |
| `custom_renderers` | Any | - |  |

#### Event Triggers

Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/

Component-specific event triggers:

| Event Trigger | Description |
| --- | --- |
| `on_cell_activated` | Fired when a cell is activated. |
| `on_cell_clicked` | Fired when a cell is clicked. |
| `on_cell_context_menu` | Fired when a cell is right-clicked. |
| `on_cell_edited` | Fired when a cell is edited. |
| `on_group_header_clicked` | Fired when a group header is clicked. |
| `on_group_header_context_menu` | Fired when a group header is right-clicked. |
| `on_group_header_renamed` | Fired when a group header is renamed. |
| `on_header_clicked` | Fired when a header is clicked. |
| `on_header_context_menu` | Fired when a header is right-clicked. |
| `on_header_menu_click` | Fired when a header menu item is clicked. |
| `on_item_hovered` | Fired when an item is hovered. |
| `on_delete` | Fired when a selection is deleted. |
| `on_finished_editing` | Fired when editing is finished. |
| `on_row_appended` | Fired when a row is appended. |
| `on_grid_selection_change` | Fired when the grid selection changes. Will pass the current selection, the selected columns and the selected rows. |
| `on_selection_cleared` | Fired when the selection is cleared. |
| `on_column_resize` | Fired when a column is resized. |
| `on_search_close` | Fired when the search close button is clicked. |
