Tiempo 0:35–1:15
En esta lección solo vas a construir una pantalla fija, todavía sin estado. Al final verás, dentro de una tarjeta con esquinas redondeadas, una cabecera con el título "Customers" y una descripción.
tutorial_261019/ # raíz del proyecto (aquí se ejecutan los comandos de reflex)
├── rxconfig.py # nombre de la app y configuración de plugins
├── pyproject.toml / uv.lock # dependencias de Python (las gestiona uv)
├── assets/ # archivos estáticos como imágenes → se referencian como "/favicon.ico"
├── reflex.lock/ # archivos de bloqueo de los paquetes de la interfaz (se suben al repositorio)
├── .web/ # código React compilado (generado automáticamente, no lo edites)
└── tutorial_261019/ # paquete de Python con el mismo nombre que app_name
├── __init__.py
└── tutorial_261019.py # el archivo en el que vamos a trabajar (app = rx.App())rxconfig.py tiene este aspecto. app_name debe coincidir con el nombre de la carpeta interior para que Reflex encuentre la app.
import reflex as rx
config = rx.Config(
app_name="tutorial_261019",
plugins=[
rx.plugins.SitemapPlugin(),
rx.plugins.TailwindV4Plugin(),
rx.plugins.RadixThemesPlugin(), # aporta el diseño base de botones, tablas, etc.
],
)Borra todo el contenido de tutorial_261019/tutorial_261019.py y empieza así:
import reflex as rx
def index() -> rx.Component:
return rx.heading("Customers")
app = rx.App()
app.add_page(index, title="Customer Data App")index() es una función que devuelve un trozo de pantalla (un componente). Una página = una función.app = rx.App() tiene que estar en el nivel superior del módulo. Reflex busca la app por ese nombre de variable.index, add_page(index) la registra en la ruta raíz /. title es el título de la pestaña del navegador.Compruébalo en el navegador — Al guardar, solo queda el título "Customers" arriba a la izquierda. Es normal que aparezca pegado a la esquina de la pantalla, porque todavía no tiene márgenes.
localhost:3000
Vista previaEl código de pantalla de Reflex se reduce, en la práctica, a estas dos reglas:
nombre=valor, por ejemplo size="6" o margin="0".rx.box( # <div>
rx.heading("Customers", size="6"), # hijo 1
rx.badge("2"), # hijo 2
display="flex", # estilo
gap="12px",
)align-items → align_items, margin-top → margin_top._ al final: as → as_, type → type_.rx.color("slate", 11) es la paleta de colores de Radix. Cuanto mayor es el número, más oscuro el tono, y se ajusta solo en modo oscuro. Normalmente se usan 1–2 para fondos, 4–6 para bordes y 11–12 para texto.Nota para el instructor — Se entiende enseguida si lo explicas como "etiquetas HTML convertidas en llamadas a funciones": rx.box = <div>, rx.text = <p>, rx.heading = de <h1> a <h6>. Muestra en las herramientas para desarrolladores del navegador (F12) las etiquetas que se generan de verdad.
La cabecera se divide en una parte izquierda (título + insignia + descripción) y una derecha (botón). El botón lo añadiremos en la lección 4; por ahora solo hacemos la parte izquierda.
def index() -> rx.Component:
return rx.box(
rx.box(
rx.box(
rx.heading("Customers", size="6", as_="h3", margin="0"),
rx.badge("2", variant="soft", color_scheme="gray", radius="full"),
display="flex",
align_items="center",
gap="12px",
),
rx.text(
"Your customer directory and audience breakdown.",
size="2",
color=rx.color("slate", 11),
margin_top="6px",
),
),
display="flex",
align_items="center",
justify_content="space-between",
flex_wrap="wrap",
gap="16px",
padding="24px",
)"2" de la insignia es un valor provisional. En la lección 3 lo cambiaremos por State.users.length() para mostrar el número real de clientes.justify_content="space-between" separa los hijos hacia los dos extremos. Por eso el botón quedará pegado al borde derecho.flex_wrap="wrap" pasa el botón a la línea siguiente cuando la pantalla es estrecha (adaptación a móvil).Compruébalo en el navegador — Junto al título aparece una insignia gris redondeada (2) y, debajo, la descripción en gris. Todavía no hay borde de tarjeta.
localhost:3000
Vista previaYour customer directory and audience breakdown.
Envuelve la cabecera en otro rx.box para darle forma de tarjeta. Esta caja exterior será el marco de toda la app, y más adelante la tabla y el gráfico irán entrando en ella, uno tras otro.
def index() -> rx.Component:
return rx.box(
header, # el box de la cabecera creado en 2-4
# aquí se añadirán la tabla (lección 3) y el gráfico (lección 5)
width="100%",
min_width="0",
max_width="64em", # ancho máximo de la tarjeta
margin_x="auto", # centrado
margin_y="24px",
border=f"1px solid {rx.color('slate', 5)}",
border_radius="16px",
background=rx.color("slate", 1),
overflow="hidden", # evita que el contenido se salga de las esquinas redondeadas
box_shadow="0 4px 24px rgba(0, 0, 0, 0.03)",
style={
"& *": {"box_sizing": "border-box"},
"& p, & h3": {"text_align": "left"},
},
)style={...} se usa para selectores CSS que son difíciles de expresar con argumentos de palabra clave. "& *" significa "todos los elementos dentro de esta caja".rx.color(...) dentro de un f-string. Ten cuidado al combinar comillas simples y dobles.Compruébalo en el navegador — La cabecera queda dentro de una tarjeta con esquinas redondeadas, centrada en la pantalla. Si estrechas la ventana del navegador, la tarjeta también se estrecha.
localhost:3000
Vista previaYour customer directory and audience breakdown.
border_radius a "4px" y a "32px" y compara la diferencia.rx.color_mode.button() a la cabecera y comprueba que los colores también cambian con naturalidad en modo oscuro.← Anterior
Lección 1 · Conoce Reflex y prepara tu entorno
Siguiente →
Lección 3 · State y Vars: que los datos dibujen la pantalla
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.