R

ReflexKR

Documentación de Reflex

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

El taller de componentes: los 100 componentes de Reflex
Superposiciones

Drawer

rx.drawer.rootrx.drawer.triggerrx.drawer.overlayrx.drawer.portalrx.drawer.contentrx.drawer.close

Documentación oficial

Pedido del taller

En la pantalla de la tienda en el móvil, al pulsar el botón «Filtros», haz que suba un panel desde abajo para elegir el orden y las condiciones de stock.

rx.drawer es un panel que entra deslizándose desde un borde de la pantalla. Sirve para sacar un momento contenido grande, como un menú lateral o un panel de selección que sube desde abajo. También se puede cerrar arrastrándolo con el dedo o el ratón. Reflex construye este componente con la librería Vaul.

  • drawer.root: envuelve todas las partes. Aquí van la dirección (direction) y el estado de apertura.
  • drawer.trigger: envuelve el elemento que abre el drawer al pulsarlo.
  • drawer.overlay: la capa semitransparente que cubre el fondo mientras está abierto.
  • drawer.portal: dibuja el contenido en la capa más externa de la página para que ningún otro elemento lo tape.
  • drawer.content: el panel que entra.
  • drawer.close: envuelve un elemento que cierra el drawer al pulsarlo.

Por defecto, drawer.content se coloca cubriendo toda la pantalla (position: fixed, los cuatro lados a 0). Por eso el tamaño del panel se fija liberando el lado opuesto con "auto" y dando el ancho o el alto a mano. Para un panel que sale por la izquierda, da right="auto" y un width.

rx.drawer.root(
    rx.drawer.trigger(rx.button("Open Drawer")),
    rx.drawer.overlay(z_index="5"),
    rx.drawer.portal(
        rx.drawer.content(
            rx.flex(
                rx.drawer.close(rx.box(rx.button("Close"))),
                align_items="start",
                direction="column",
            ),
            top="auto",
            right="auto",
            height="100%",
            width="20em",
            padding="2em",
            background_color=rx.color("gray", 2),
        )
    ),
    direction="left",
)

Prueba las propiedades

Cambia el ancho y el color de un panel que sale por la derecha (direction="right", left="auto"). Si desactivas modal, con el drawer abierto se puede pulsar y hacer scroll en el fondo.

Prueba las propiedades

title

width

color

modal

rx.drawer.root(
    rx.drawer.trigger(rx.button("Open panel")),
    rx.drawer.overlay(),
    rx.drawer.portal(
        rx.drawer.content(
            rx.flex(
                rx.drawer.title("Settings"),
                rx.drawer.description("Drag it to the right or press Esc to close."),
                rx.drawer.close(rx.button("Close", size="1")),
                direction="column",
                align_items="start",
                spacing="3",
            ),
            left="auto",
            height="100%",
            width="20em",
            padding="2em",
            background_color=rx.color("gray", 3),
        ),
    ),
    direction="right",
    modal=True,
)
El tamaño y el color del panel se fijan con propiedades de estilo de drawer.content. modal va en drawer.root.
Propiedad (root)ValoresPor defectoUso
direction"top" "bottom" "left" "right""bottom"Dirección por la que entra. También decide hacia dónde se arrastra para cerrar
open · default_openTrue / False-Decidir la apertura desde el State (controlado) · abierto al principio
modalTrue / FalseTrueCon False, se puede pulsar y hacer scroll fuera mientras está abierto
dismissibleTrue / False-Con False, no se cierra arrastrando, pulsando fuera ni con Esc (se usa junto con open)
handle_onlyTrue / False-Con True, solo se puede arrastrar desde drawer.handle
snap_pointsLista de números entre 0 y 1 o de "px"-Alturas en las que se detiene (solo valores Python, no valores del State)
fade_from_indexEnteroÚltimo puntoA partir de qué punto de parada se oscurece el fondo
close_threshold0~1-Cuánto hay que arrastrar para que se cierre
scroll_lock_timeoutMilisegundos500Tiempo que se bloquea el arrastre tras hacer scroll dentro del panel
should_scale_backgroundTrue / False-Efecto de encoger un poco el fondo (requiere vaul-drawer-wrapper)
prevent_scroll_restorationTrue / FalseTrueEvitar que se restaure la posición de scroll al cerrarse
on_open_changeEvento-Al abrirse o cerrarse. Pasa el nuevo valor de apertura (bool)
on_animation_endEvento-Al terminar la animación de apertura o cierre. Pasa el valor de apertura de ese momento

drawer.content tiene los eventos on_open_auto_focus, on_close_auto_focus, on_escape_key_down, on_pointer_down_outside y on_interact_outside. Las demás partes apenas aceptan as_child.

Los cuatro lados

Cada direction pega el panel a un lado distinto, así que hay que liberar el lado opuesto con "auto". Los paneles que salen por arriba o por abajo fijan el alto en lugar del ancho. Como los cuatro drawers se construyen igual, los agrupamos en una pequeña función.

def drawer_edge_demo(side: str, **position):
    return rx.drawer.root(
        rx.drawer.trigger(rx.button(side, variant="soft")),
        rx.drawer.overlay(),
        rx.drawer.portal(
            rx.drawer.content(
                rx.flex(
                    rx.drawer.title(f"From the {side}"),
                    rx.drawer.close(rx.button("Close", size="1")),
                    direction="column",
                    align_items="start",
                    spacing="3",
                ),
                padding="2em",
                background_color=rx.color("gray", 2),
                **position,
            ),
        ),
        direction=side,
    )


rx.flex(
    drawer_edge_demo("left", right="auto", width="16em"),
    drawer_edge_demo("right", left="auto", width="16em"),
    drawer_edge_demo("top", bottom="auto", height="30%"),
    drawer_edge_demo("bottom", top="auto", height="30%"),
    spacing="3",
    wrap="wrap",
)

Título, descripción y asa

No aparecen en la lista de partes de la documentación oficial, pero en 0.10 también existen drawer.title, drawer.description y drawer.handle. Los lectores de pantalla leen el título y la descripción, y el asa dibuja una barra arrastrable en la parte superior del panel. Con handle_only=True solo se puede arrastrar desde el asa, así es más difícil cerrarlo sin querer al pulsar los botones de dentro.

rx.drawer.root(
    rx.drawer.trigger(rx.button("Open sheet")),
    rx.drawer.overlay(),
    rx.drawer.portal(
        rx.drawer.content(
            rx.flex(
                rx.drawer.handle(),
                rx.drawer.title("Share this page"),
                rx.drawer.description("Drag the handle down to close the sheet."),
                rx.flex(
                    rx.button(rx.icon("link"), "Copy link", variant="soft"),
                    rx.button(rx.icon("mail"), "Email", variant="soft"),
                    spacing="3",
                ),
                direction="column",
                spacing="3",
                width="100%",
            ),
            top="auto",
            height="auto",
            padding="1.5em",
            background_color=rx.color("gray", 2),
            border_radius="12px 12px 0 0",
        ),
    ),
    handle_only=True,
)

Abrir y cerrar desde el State: menú lateral

Si conectas un valor del State a open, el código puede cerrar el drawer al pulsar un enlace. Conecta también on_open_change para que los cambios de apertura hechos con el botón trigger, el botón Close o la tecla Esc vuelvan al State. Como es modal=False, se puede hacer scroll en la página con el drawer abierto, y al pulsar un enlace el drawer se cierra y lleva a esa sección.

Section 1

Section 2

class DrawerSidebarState(rx.State):
    is_open: bool = False

    @rx.event
    def set_is_open(self, value: bool):
        self.is_open = value

    @rx.event
    def close_drawer(self):
        self.is_open = False


rx.vstack(
    rx.drawer.root(
        rx.drawer.trigger(rx.button("Open Drawer")),
        rx.drawer.overlay(),
        rx.drawer.portal(
            rx.drawer.content(
                rx.flex(
                    rx.drawer.close(rx.button("Close")),
                    rx.link("Section 1", href="#drawer-section-1", on_click=DrawerSidebarState.close_drawer),
                    rx.link("Section 2", href="#drawer-section-2", on_click=DrawerSidebarState.close_drawer),
                    align_items="start",
                    direction="column",
                    spacing="3",
                ),
                right="auto",
                height="100%",
                width="16em",
                padding="2em",
                background_color=rx.color("grass", 4),
            ),
        ),
        open=DrawerSidebarState.is_open,
        on_open_change=DrawerSidebarState.set_is_open,
        direction="left",
        modal=False,
    ),
    rx.section(rx.heading("Section 1", size="5"), id="drawer-section-1", height="160px", padding="1em 0"),
    rx.section(rx.heading("Section 2", size="5"), id="drawer-section-2", height="160px", padding="1em 0"),
)

Completar el pedido

Un panel de filtros que sube desde abajo. Lo que se elige con los interruptores y los botones de radio entra al momento en el State, y al pulsar «Show results» el handler baja el panel con self.open = False. Si se cierra arrastrándolo hacia abajo, los valores se conservan igual.

Sort: Newest

class DrawerFilterState(rx.State):
    open: bool = False
    in_stock: bool = False
    sort: str = "Newest"

    @rx.event
    def set_open(self, value: bool):
        self.open = value

    @rx.event
    def set_in_stock(self, value: bool):
        self.in_stock = value

    @rx.event
    def set_sort(self, value: str):
        self.sort = value

    @rx.event
    def apply(self):
        self.open = False


rx.flex(
    rx.text("Sort: ", DrawerFilterState.sort, rx.cond(DrawerFilterState.in_stock, " · in stock only", ""), size="2"),
    rx.drawer.root(
        rx.drawer.trigger(rx.button(rx.icon("sliders-horizontal"), "Filters", variant="outline")),
        rx.drawer.overlay(),
        rx.drawer.portal(
            rx.drawer.content(
                rx.flex(
                    rx.drawer.handle(),
                    rx.drawer.title("Filters"),
                    rx.flex(rx.switch(checked=DrawerFilterState.in_stock, on_change=DrawerFilterState.set_in_stock),
                            rx.text("In stock only", size="2"), spacing="2", align="center"),
                    rx.radio(["Newest", "Price: low", "Price: high"], value=DrawerFilterState.sort,
                             on_change=DrawerFilterState.set_sort, direction="row"),
                    rx.button("Show results", on_click=DrawerFilterState.apply, width="100%"),
                    direction="column",
                    spacing="4",
                    width="100%",
                    max_width="480px",
                    margin="0 auto",
                ),
                top="auto",
                height="auto",
                padding="1.5em",
                background_color=rx.color("gray", 2),
                border_radius="12px 12px 0 0",
            ),
        ),
        open=DrawerFilterState.open,
        on_open_change=DrawerFilterState.set_open,
        direction="bottom",
    ),
    spacing="3",
    align="center",
    wrap="wrap",
)

Notas del taller

  • root(direction) > trigger · overlay · portal > content > close. La dirección por defecto es bottom.

  • content se coloca cubriendo toda la pantalla, así que libera el lado opuesto con "auto" y fija width o height.

  • Si lo controlas con open + on_open_change, el handler de un enlace o un botón puede cerrar el drawer.

  • modal=False permite usar el fondo con el drawer abierto, y handle_only=True solo deja arrastrar desde el asa.

  • snap_points se fija al compilar, así que no admite valores del State.

← Anterior

Superposiciones · Alert Dialog

Siguiente →

Superposiciones · Dropdown Menu

Recibe avisos de nuevas lecciones

Te avisaremos por correo cuando este curso tenga lecciones nuevas o se abra un curso de continuación.

Datos: tu correo electrónico · Finalidad: avisos de nuevas lecciones o de la publicación de este curso · Conservación: hasta que pidas que lo borremos. No lo usamos para nada más ni lo compartimos. Responde a cualquier aviso y lo borraremos enseguida.

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.

Se muestra como texto plano, sin enlaces ni formato.

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
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex