El taller de componentes: los 100 componentes de Reflex
·
Resumen del curso
Diseño
·
Box
·
Flex
·
Stack
·
Grid
·
Container
·
Section
·
Center
·
Spacer
·
Separator
·
Card
·
Inset
·
Aspect Ratio
·
Fragment
Tipografía
·
Text
·
Heading
·
Link
·
Code
·
Markdown
·
Blockquote
·
Quote
·
Strong
·
Em
·
Kbd
Formularios
·
Button
·
Input
·
Text Area
·
Checkbox
·
Switch
·
Radio Group
·
Select
·
Slider
·
Form
·
Upload
Visualización de datos
·
Badge
·
Avatar
·
Icon
·
Callout
·
List
·
Data List
·
Code Block
·
Progress
·
Spinner
·
Scroll Area
·
Moment
Mostrar y ocultar
·
Tabs
·
Accordion
·
Segmented Control
Superposiciones
·
Tooltip
·
Popover
·
Hover Card
·
Dialog
·
Alert Dialog
·
Drawer
·
Dropdown Menu
·
Context Menu
·
Toast
Renderizado dinámico
·
Cond
·
Match
·
Foreach
·
Auto Scroll
Multimedia
·
Image
·
Audio
·
Video
Tablas y cuadrículas de datos
·
Table
·
Data Table
·
Data Editor
Gráficos
·
Area Chart
·
Bar Chart
·
Line Chart
·
Composed Chart
·
Pie Chart
·
Radar Chart
·
Radial Bar Chart
·
Scatter Chart
·
Funnel Chart
·
Treemap
·
Sankey Chart
·
Error Bar
Piezas de gráficos
·
Axis
·
Cartesian Grid
·
Chart Tooltip
·
Legend
·
Label
·
Reference
·
Brush
Otras bibliotecas de gráficos
·
Plotly
·
Pyplot
Elementos HTML
·
HTML
·
HTML Text
·
HTML Layout
·
HTML Forms
·
HTML Media
·
HTML Tables
·
SVG
Otras herramientas
·
Theme
·
Skeleton
·
Clipboard
·
Html Embed
·
Script
·
Memo
Pedido del taller
Haz que la tabla de existencias de la cafetería se pueda editar pulsando directamente en las celdas, como en Excel, y que el valor total del inventario se recalcule con cada cambio.
rx.data_editor es una cuadrícula de datos en la que, como en una hoja de cálculo, se eligen celdas y se editan. Se dibuja con Glide Data Grid, así que va rápida aunque tenga muchísimas filas y columnas. Para una tabla de solo lectura basta con Data Table; este componente es para cuando el usuario tiene que modificar valores.
columns es una lista con un dict por columna.
title: el título de la columna.id: el id de la columna. Si falta, es el title en minúsculas.width: el ancho de la columna (px).type: el tipo de celda. Se usan "str" (por defecto), "int", "float", "bool" e "image".group: dibuja un título de grupo extra sobre las columnas que comparten ese valor.data es una lista con una lista por fila. Cada valor debe coincidir con el type de su columna.
rx.data_editor(
columns=[
{"title": "Code", "type": "str"},
{"title": "Value", "type": "int"},
{"title": "Activated", "type": "bool"},
],
data=[
["A", 1, True],
["B", 2, False],
["C", 3, False],
["D", 4, True],
["E", 5, True],
["F", 6, False],
],
)Pulsa en la cuadrícula para seleccionar celdas, filas y columnas. Con doble clic en una celda se abre el editor (este banco de pruebas no guarda los valores, así que lo editado vuelve enseguida a su valor original).
Prueba las propiedades
row_markers
column_select
row_select
range_select
vertical_border
show_search
rx.data_editor(
columns=[
{"title": "Code", "type": "str"},
{"title": "Value", "type": "int"},
{"title": "Activated", "type": "bool"},
],
data=[["A", 1, True], ["B", 2, False], ["C", 3, False], ["D", 4, True]],
row_markers="number",
column_select="single",
row_select="multi",
range_select="rect",
vertical_border=True,
show_search=False,
height="220px",
)| Propiedad | Valores | Uso |
|---|---|---|
columns · data | Lista | Configuración de columnas, datos de las filas |
rows | Entero | Número de filas. Si falta, la longitud de data |
row_height · header_height · group_header_height | Entero (px) | Alto de las filas, de los títulos de columna y de los títulos de grupo |
row_markers | "none" "number" "checkbox" "both" "checkbox-visible" "clickable-number" | Marca delante de cada fila |
row_marker_start_index · row_marker_width | Entero | Número inicial de fila, ancho de la columna de marcas |
freeze_columns | Entero | Columnas fijas a la izquierda al hacer scroll horizontal |
min_column_width · max_column_width · max_column_auto_width | Entero (px) | Límites del ancho de columna |
column_select · row_select | "none" "single" "multi" | Selección de columnas y filas |
range_select | "none" "cell" "rect" "multi-cell" "multi-rect" | Selección de rangos de celdas |
*_selection_blending · row_selection_mode · span_range_behavior | Cadena | Detalles como si se combinan varios tipos de selección |
smooth_scroll_x · smooth_scroll_y | True / False | Scroll suave |
vertical_border · draw_focus_ring · fixed_shadow_x · fixed_shadow_y | True / False | Líneas verticales, borde de foco, sombra de las zonas fijas |
fill_handle | True / False | Asa en la esquina de la celda para rellenar valores arrastrando |
get_cells_for_selection | True / False | Permitir copiar las celdas seleccionadas (True por defecto) |
overscroll_x · overscroll_y · scroll_offset_x · scroll_offset_y | Entero | Margen de scroll más allá del final, posición de scroll inicial |
prevent_diagonal_scrolling · show_search | True / False | Impedir el scroll en diagonal, búsqueda |
theme | rx.data_editor_theme(...) o dict | Colores y tipografía |
grid_selection | dict de selección | Selección actual. Si usas on_grid_selection_change, conéctalos siempre juntos |
| Evento | Recibe | Cuándo |
|---|---|---|
on_cell_clicked · on_cell_activated · on_cell_context_menu | [columna, fila] | Al pulsar una celda, al activarla con Enter o doble clic, al hacer clic derecho |
on_cell_edited | [columna, fila], dict de la nueva celda | Al editar el valor de una celda (el nuevo valor está en dict["data"]) |
on_finished_editing | dict de la nueva celda, dirección de avance | Al cerrarse el editor |
on_header_clicked · on_header_context_menu | [columna, fila] | Al pulsar un título de columna, al hacer clic derecho en él |
on_header_menu_click | Columna, posición | Al pulsar el botón de menú de un título de columna |
on_group_header_clicked · on_group_header_context_menu · on_group_header_renamed | - | Al pulsar un título de grupo, al hacer clic derecho en él, al renombrarlo |
on_item_hovered | [columna, fila] | Cuando el ratón pasa por encima de una celda |
on_grid_selection_change · on_selection_cleared | dict de selección · - | Cuando la selección cambia o se borra |
on_delete | dict de selección | Al pulsar la tecla Supr en las celdas seleccionadas |
on_row_appended · on_column_resize · on_search_close | - · columna, ancho · - | Añadir una fila, cambiar el ancho de una columna, cerrar la búsqueda |
Las columnas con type="image" muestran la dirección de una imagen como una miniatura. El valor puede ser una sola dirección o una lista de direcciones con varias imágenes. Sube row_height para que se vean bien. Al pulsar una celda de imagen se abre una ventana para verla en grande.
rx.data_editor(
columns=[
{"title": "Preview", "type": "image", "width": 120},
{"title": "Name", "type": "str"},
],
data=[
["https://picsum.photos/id/237/160/100", "Puppy"],
["https://picsum.photos/id/1025/160/100", "Pug"],
[["https://picsum.photos/id/1084/160/100", "https://picsum.photos/id/1074/160/100"], "Two animals"],
],
row_height=90,
height="320px",
)on_cell_clicked pasa la posición de la celda pulsada como [columna, fila] (ambas empiezan en 0). La configuración de columnas de abajo tiene group: "Data", así que sobre las cinco columnas que no son Title aparece un título de grupo extra, «Data».
from typing import Any
class DataEditorClickState(rx.State):
clicked_data: str = "Cell clicked: "
cols: list[dict[str, Any]] = [
{"title": "Title", "type": "str"},
{"title": "Name", "type": "str", "group": "Data", "width": 220},
{"title": "Birth", "type": "str", "group": "Data", "width": 150},
{"title": "Human", "type": "bool", "group": "Data", "width": 80},
{"title": "House", "type": "str", "group": "Data"},
{"title": "Patronus", "type": "str", "group": "Data"},
]
data: list[list[Any]] = [
["1", "Harry James Potter", "31 July 1980", True, "Gryffindor", "Stag"],
["2", "Ronald Bilius Weasley", "1 March 1980", True, "Gryffindor", "Jack Russell terrier"],
["3", "Hermione Jean Granger", "19 September 1979", True, "Gryffindor", "Otter"],
["4", "Rubeus Hagrid", "6 December 1928", False, "Gryffindor", "None"],
]
@rx.event
def click_cell(self, pos: tuple[int, int]):
col, row = pos
self.clicked_data = f"Cell clicked: column {col}, row {row}"
rx.vstack(
rx.heading(DataEditorClickState.clicked_data, size="3"),
rx.data_editor(
columns=DataEditorClickState.cols,
data=DataEditorClickState.data,
on_cell_clicked=DataEditorClickState.click_cell,
height="220px",
),
width="100%",
)Data Editor solo permite editar las celdas; no guarda lo editado por sí mismo. Para que el cambio quede en pantalla, el handler de on_cell_edited tiene que recibir la posición y la información de la nueva celda y modificar el data del State. El nuevo valor está en new_value["data"]. Edita un valor con doble clic en una celda o pulsa la casilla de Activated.
La documentación oficial no trae ejemplo de este evento, pero es imprescindible para hacer una cuadrícula editable.
Last edit: nothing yet
from typing import Any
class DataEditorEditState(rx.State):
cols: list[dict[str, Any]] = [
{"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]]
last_edit: str = "nothing yet"
@rx.event
def edit_cell(self, pos: tuple[int, int], new_value: dict[str, Any]):
col, row = pos
rows = [list(r) for r in self.data]
rows[row][col] = new_value.get("data")
self.data = rows
self.last_edit = f"row {row}, column {col} -> {new_value.get('data')}"
@rx.event
def add_row(self):
self.data = self.data + [["New", 0, False]]
rx.vstack(
rx.data_editor(
columns=DataEditorEditState.cols,
data=DataEditorEditState.data,
on_cell_edited=DataEditorEditState.edit_cell,
row_markers="number",
height="200px",
),
rx.hstack(
rx.button("Add row", size="1", on_click=DataEditorEditState.add_row),
rx.text("Last edit: ", DataEditorEditState.last_edit, size="2"),
align="center",
),
width="100%",
)En theme van los colores y la tipografía. Pasa argumentos con nombre a rx.data_editor_theme(...) (en la documentación oficial, from reflex.components.datadisplay.dataeditor import DataEditorTheme) o pasa directamente un dict. Las claves valen tanto con guiones bajos, como accent_color, como en camelCase, como accentColor. Abajo, un tema oscuro con row_height=50, scroll suave y selección de una columna cada vez.
dataeditor_dark_theme = {
"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",
}
rx.data_editor(
columns=[
{"title": "Code", "type": "str", "width": 120},
{"title": "Value", "type": "int", "width": 120},
{"title": "Activated", "type": "bool", "width": 120},
],
data=[["A", 1, True], ["B", 2, False], ["C", 3, False], ["D", 4, True], ["E", 5, True]],
row_height=50,
smooth_scroll_x=True,
smooth_scroll_y=True,
column_select="single",
theme=rx.data_editor_theme(**dataeditor_dark_theme),
height="260px",
)La tabla de existencias guarda los cambios con on_cell_edited, y el valor total del inventario y el número de productos que hay que volver a pedir son variables calculadas. Al cambiar una cantidad o un precio unitario, los badges de abajo cambian al momento. La columna de artículos se fija con freeze_columns=1.
from typing import Any
class DataEditorStockState(rx.State):
cols: list[dict[str, Any]] = [
{"title": "Item", "type": "str", "width": 160},
{"title": "Qty", "type": "int", "width": 80},
{"title": "Unit price", "type": "float", "width": 110},
{"title": "Reorder", "type": "bool", "width": 90},
]
data: list[list[Any]] = [
["Coffee beans", 12, 18.5, False],
["Paper cups", 300, 0.1, False],
["Oat milk", 4, 2.9, True],
]
@rx.event
def edit_cell(self, pos: tuple[int, int], new_value: dict[str, Any]):
col, row = pos
rows = [list(r) for r in self.data]
rows[row][col] = new_value.get("data")
self.data = rows
@rx.var
def total(self) -> str:
value = sum(float(r[1] or 0) * float(r[2] or 0) for r in self.data)
return f"${value:,.2f}"
@rx.var
def reorder_count(self) -> int:
return sum(1 for r in self.data if r[3])
rx.vstack(
rx.data_editor(
columns=DataEditorStockState.cols,
data=DataEditorStockState.data,
on_cell_edited=DataEditorStockState.edit_cell,
row_markers="number",
freeze_columns=1,
height="180px",
),
rx.hstack(
rx.badge("Stock value: ", DataEditorStockState.total, size="2"),
rx.badge("To reorder: ", DataEditorStockState.reorder_count, color_scheme="orange", size="2"),
),
width="100%",
)Notas del taller
columns es una lista de dicts {title, type, width, id, group}; data, una lista de filas. Los valores deben coincidir con el type de la columna.
type: str · int · float · bool · image.
Lo editado solo se conserva si on_cell_edited((columna, fila), nueva celda) modifica el data del State.
Qué se puede seleccionar: column_select · row_select · range_select; la marca delante de cada fila: row_markers.
theme=rx.data_editor_theme(...) o un dict; el tamaño, con height y row_height.
Recibe avisos de nuevas lecciones
Te avisaremos por correo cuando este curso tenga lecciones nuevas o se abra un curso de continuación.
R
ReflexKR
Aprende Python y Reflex haciendo. Todos los ejemplos se ejecutaron y verificaron en Reflex 0.10.0.
ReflexKR es gratis y sin banners publicitarios. Si te ayudó, puedes apoyarlo con un café. ☕ Invítanos un café
Aviso de afiliados© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Preguntas y respuestas
Pregunta lo que quieras sobre esta lección. Aquí se reúnen las preguntas de todas las versiones de idioma de esta página.