---
title: "Cómo Añadir Múltiples Páginas a NiceGUI (Guía 2026)"
description: "Aprende a añadir múltiples páginas a tu app de NiceGUI con enrutado @ui.page(), layouts compartidos y navegación SPA."
date: 2024-03-18
categories: ["web-development"]
tags: ["nicegui","python","ui-framework"]
---

import YouTubeEmbed from "@components/widgets/YouTubeEmbed.astro";
import Notice from "@components/widgets/Notice.astro";
import ListCheck from "@components/widgets/ListCheck.astro";
import Tabs from "@components/widgets/Tabs.astro";
import Tab from "@components/widgets/Tab.astro";
import Accordion from "@components/widgets/Accordion.astro";
import Button from "@components/widgets/Button.astro";


NiceGUI (más de 16k estrellas en GitHub) es un framework de UI en Python para construir interfaces web en Python, sin necesidad de JavaScript. La mayoría de las apps que van más allá de un prototipo rápido necesitan múltiples páginas con navegación compartida: un header, una barra lateral y contenido que cambia según la URL.

Esta guía cubre tres enfoques para el enrutado de páginas en NiceGUI: el clásico `@ui.page()` con un frame compartido, la navegación estilo SPA con `ui.sub_pages` y el enrutado modular con `APIRouter`. Elige el que se ajuste a la complejidad de tu app.

<Notice type="info" title="Actualizado para NiceGUI v3.15">
Esta guía se actualizó para NiceGUI v3.x (probada con v3.15.0). Si migras desde v1.x o v2.x, consulta la [guía oficial de migración a v3](https://zenodo.org/records/17259155). Cambios clave: se requiere Python 3.10+, se eliminó la API `.tailwind`, `ui.open()` fue reemplazado por `ui.navigate.to()` y el cliente compartido auto-index ya no existe.
</Notice>

Si eres nuevo en NiceGUI, empieza con la guía de [primeros pasos con NiceGUI](/es/empezar-nicegui/). Si estás evaluando frameworks de UI en Python, la [comparación Streamlit vs NiceGUI](/es/streamlit-vs-nicegui/) o nuestro resumen de los [mejores frameworks web de Python](/es/mejores-frameworks-web-python/) pueden ayudarte a decidir.

El vídeo de abajo muestra el enfoque original con NiceGUI v1.x. Los patrones de código siguen aplicando. El enfoque de `@ui.page()` + frame con `@contextmanager` sigue siendo el patrón idiomático de NiceGUI. Consulta las notas específicas de v3 a lo largo de esta guía.

<YouTubeEmbed
  url="https://www.youtube.com/embed/bW3ifL2hdfc"
  label="Cómo añadir múltiples páginas a NiceGUI"
/>

## Requisitos previos

Antes de empezar, asegúrate de tener lo siguiente:

<ListCheck>
<ul>
  <li>Python 3.10 o más reciente (`python3 --version` para comprobarlo)</li>
  <li>pip (incluido con Python) o [configura un proyecto Python con uv](/es/empezar-uv/) para instalaciones más rápidas</li>
  <li>Un editor de texto o IDE (se recomienda VS Code con la extensión de NiceGUI para el autocompletado de Tailwind)</li>
  <li>~10 minutos de tiempo</li>
</ul>
</ListCheck>

Instala NiceGUI fijando la versión:

```bash
pip install "nicegui>=3.0.0"
```

O con uv:

```bash
uv pip install "nicegui>=3.0.0"
```

Verifica la instalación:

```bash
pip show nicegui
```

Deberías ver `Version: 3.x.x` en la salida. Si ves una versión inferior a 3.0, actualiza con `pip install --upgrade nicegui`.

## Estructura del proyecto para una app NiceGUI multipágina

Mantener los archivos organizados desde el principio te ahorra dolores de cabeza cuando añadas más páginas después. Esta es la estructura de carpetas que vamos a construir:

```
my_nicegui_app/
├── main.py              # Entry point — starts the server
├── theme.py             # Shared layout frame (header, drawer, footer)
├── menu.py              # Navigation menu links
├── home_page.py         # Home page content
├── all_pages.py         # Route registration
├── pages/
│   ├── __init__.py      # Empty — makes pages a proper Python package
│   ├── title_generator.py
│   └── script_generator.py
├── requirements.txt
└── .gitignore
```

Cada archivo tiene una sola tarea. `theme.py` define el layout compartido. `menu.py` define los enlaces de navegación. La carpeta `pages/` contiene el contenido de cada página. `all_pages.py` conecta las rutas. `main.py` lo arranca todo.

<Notice type="warning" title="Aviso sobre el repo de inicio">
El repo <a href="https://github.com/bitdoze/nicegui-starter">bitdoze/nicegui-starter</a> está pensado para NiceGUI v1.x. Usa el código de esta guía para compatibilidad con v3.x.
</Notice>

### Dependencias y fijado de versiones

Fija la versión mayor en `requirements.txt`. Quita `numpy` a menos que la uses (las páginas de ejemplo de aquí no la usan):

```
nicegui>=3.0.0
```

No te saltes `pages/__init__.py`. Puede ser un archivo vacío, pero es necesario para un empaquetado correcto de Python. Herramientas como PyInstaller y Nuitka lo requieren.

## Enfoque 1: enrutado de páginas con @ui.page() y un frame compartido

Este es el patrón de enrutado de páginas "tradicional" de NiceGUI. Cada URL tiene su propio decorador `@ui.page()`, y todas comparten un layout mediante un frame con `@contextmanager`. Úsalo cuando:

- Quieres instancias de página independientes (cada cliente recibe un árbol de UI nuevo)
- Las páginas tienen layouts muy diferentes
- Estás construyendo un sitio multipágina sencillo con unas pocas rutas

<Notice type="info">
El frame con `@contextmanager` sigue siendo el patrón idiomático de NiceGUI para layouts compartidos. El <a href="https://github.com/zauberzeug/nicegui/blob/main/examples/modularization/theme.py">ejemplo oficial de modularización</a> usa el mismo enfoque.
</Notice>

### Creando el layout compartido (theme.py)

Este archivo define el frame de la página: header, drawer lateral, footer y el área de contenido. Cada página llama a `theme.frame()` y coloca su contenido dentro.

```python
from contextlib import contextmanager

from menu import menu

from nicegui import ui


@contextmanager
def frame(navtitle: str):
    """Custom page frame to share the same styling and behavior across all pages"""
    ui.colors(primary='#6E93D6', secondary='#53B689', accent='#111B1E', positive='#53B689')
    with ui.column().classes('absolute-center items-center h-screen no-wrap p-9 w-full'):
        yield
    with ui.header() as header:
        ui.button(on_click=lambda: left_drawer.toggle(), icon='menu').props('flat color=white')
        ui.label('Getting Started').classes('font-bold')

    with ui.footer(value=False) as footer:
        ui.label('Footer')
    with ui.left_drawer().classes('bg-blue-100') as left_drawer:
        ui.label('Menu')
        with ui.column():
            menu()
    with ui.page_sticky(position='bottom-right', x_offset=20, y_offset=20):
        ui.button(on_click=footer.toggle, icon='contact_support').props('fab')
```

La sentencia `yield` es donde se inserta el contenido de la página. El decorador `@contextmanager` lo hace reutilizable. Cada página llama a `with theme.frame('Page Title'):` y coloca sus elementos de UI dentro del bloque.

Puntos clave:
- `ui.colors()` define el esquema de color de toda la página
- `ui.header()` crea la barra superior con un botón de menú hamburguesa
- `ui.left_drawer()` crea una barra lateral deslizante con navegación
- `ui.footer()` crea una barra inferior opcional (oculta por defecto, se alterna con el botón FAB)

### Construyendo el menú de navegación (menu.py)

```python
from nicegui import ui


def menu() -> None:
    ui.link('Home', '/').classes('text-black')
    ui.link('YouTube Titles', '/youtube-title-generator/').classes('text-black')
    ui.link('YouTube Script Generator', '/youtube-script/').classes('text-black')
```

Cada `ui.link()` crea un enlace de navegación clicable. El primer argumento es el texto visible, el segundo es la ruta URL. Para navegación programática (p. ej., tras enviar un formulario), usa `ui.navigate.to('/new-page')` en lugar de enlaces.

### Definiendo el contenido de las páginas (pages/)

Cada archivo de página define una función que usa el context manager `theme.frame()`. Toda la creación de UI ocurre dentro de la función, nunca a nivel de módulo (esto es un requisito estricto en NiceGUI v3).

**pages/title_generator.py**

```python
import theme
from nicegui import ui


def title_generator():
    with theme.frame('YouTube Title Generator'):
        ui.page_title('YouTube Title Generator')
        ui.markdown('# Title Generator')
        ui.markdown('Generate catchy titles for your YouTube videos.')
```

**pages/script_generator.py**

```python
import theme
from nicegui import ui


def script_generator():
    with theme.frame('YouTube Script Generator'):
        ui.page_title('YouTube Script Generator')
        ui.markdown('# Script Generator')
        ui.markdown('Create video scripts with AI assistance.')
```

No olvides `pages/__init__.py`. Simplemente crea un archivo vacío. Sin él, Python no tratará `pages` como un paquete.

### Registrando las rutas (all_pages.py)

Este archivo mapea las rutas URL a funciones de página usando decoradores `ui.page()`:

```python
from nicegui import ui
from pages.title_generator import title_generator
from pages.script_generator import script_generator


def create() -> None:
    @ui.page('/youtube-title-generator/')
    def title_page():
        title_generator()

    @ui.page('/youtube-script/')
    def script_page():
        script_generator()
```

La función `create()` se llama una vez desde `main.py` para registrar todas las rutas antes de que arranque el servidor.

### Juntándolo todo (main.py)

```python
import all_pages
import home_page
import theme

from nicegui import ui


@ui.page('/')
def index_page() -> None:
    with theme.frame('Homepage'):
        home_page.content()


all_pages.create()

ui.run(title='Getting Started With NiceGUI')
```

**Verifica**: Ejecuta `python main.py` y abre `http://localhost:8080`. Deberías ver el header con un menú hamburguesa, la barra lateral con los enlaces de navegación y el contenido de la página de inicio. Haz clic en cada enlace del menú para confirmar que la navegación funciona.

<Accordion label="Error: 'ui.page cannot be used in the global scope'" group="errors">
Esto ocurre cuando tienes creación de UI a nivel de módulo junto a decoradores `@ui.page()`. NiceGUI v3 eliminó el cliente compartido auto-index. TODA la UI debe estar dentro de funciones `@ui.page()`. Mueve cualquier llamada de nivel superior a `ui.element()`, `ui.label()`, etc. dentro de una función de página.
</Accordion>

## Enfoque 2: navegación SPA con ui.sub_pages

`ui.sub_pages` es el enfoque moderno de NiceGUI para apps donde quieres navegación del lado del cliente. El header y la barra lateral permanecen fijos, y solo cambia el área de contenido. Sin recarga completa de página, sin parpadeo de contenido sin estilos.

Esto es lo que la mayoría de los lectores que construyen dashboards o herramientas internas realmente quieren.

### Cuándo usar sub_pages vs @ui.page()

<Tabs>
<Tab name="sub_pages (SPA)">
- Cambio de contenido del lado del cliente, sin recarga de página
- El header y la barra lateral persisten entre navegaciones
- La URL se actualiza en la barra del navegador
- Ideal para dashboards y herramientas internas con navegación consistente
- Añadido en NiceGUI v2.22.0, estable en v3
</Tab>
<Tab name="@ui.page() (tradicional)">
- Recarga completa de página por ruta
- Cada página obtiene una instancia de cliente independiente
- Las páginas pueden tener layouts completamente diferentes
- Mejor para sitios públicos con diseños de página distintos
- Ha sido el enfoque estándar desde NiceGUI 1.x
</Tab>
</Tabs>

### Ejemplo completo de sub_pages

Aquí tienes una app SPA autocontenida. Todo va en `main.py` por claridad. En un proyecto real, separarías las funciones de página en archivos distintos:

```python
from nicegui import ui


def menu():
    ui.link('Home', '/').classes('text-black')
    ui.link('YouTube Titles', '/youtube-title-generator/').classes('text-black')
    ui.link('YouTube Script Generator', '/youtube-script/').classes('text-black')


def home():
    ui.markdown('# Welcome')
    ui.markdown('Select a tool from the sidebar.')


def title_generator():
    ui.markdown('# Title Generator')
    ui.markdown('Generate catchy titles for your YouTube videos.')


def script_generator():
    ui.markdown('# Script Generator')
    ui.markdown('Create video scripts with AI assistance.')


@ui.page('/')
def root():
    ui.colors(primary='#6E93D6', secondary='#53B689', accent='#111B1E', positive='#53B689')
    with ui.header():
        ui.button(on_click=lambda: left_drawer.toggle(), icon='menu').props('flat color=white')
        ui.label('My App').classes('font-bold')
    with ui.left_drawer().classes('bg-blue-100') as left_drawer:
        with ui.column():
            menu()
    with ui.column().classes('p-4 w-full'):
        ui.sub_pages({
            '/': home,
            '/youtube-title-generator/': title_generator,
            '/youtube-script/': script_generator,
        })


ui.run(title='My App')
```

**Verifica**: Ejecuta la app. Haz clic en los enlaces del menú. La URL debería cambiar y el área de contenido debería intercambiarse sin una recarga completa de página. El header y la barra lateral permanecen fijos. Usa el botón de retroceso del navegador para confirmar que la navegación del historial funciona.

<Accordion label="¿Por qué sub_pages muestra una página en blanco?" group="errors">
Asegúrate de que las claves del diccionario de rutas en `ui.sub_pages()` coincidan exactamente con las rutas de los enlaces del menú, incluidas las barras finales. La ruta raíz debe ser `'/'`. Verifica también que las rutas de `ui.link()` del menú coincidan con las claves del diccionario carácter por carácter.
</Accordion>

## Enfoque 3: enrutado modular con APIRouter

Para apps más grandes donde quieres agrupar páginas relacionadas bajo un prefijo de URL y mantenerlas en módulos separados, `nicegui.APIRouter` funciona como el `APIRouter` de FastAPI. Escala bien cuando tienes muchos grupos de funcionalidades y varios desarrolladores.

### Agrupando páginas con prefijos de URL compartidos

**api_pages.py**

```python
from nicegui import APIRouter, ui

router = APIRouter(prefix='/tools')


@router.page('/')
def tools_index():
    ui.markdown('# Tools')
    ui.markdown('Select a tool from the list.')


@router.page('/generator/{name}')
def generator(name: str):
    ui.markdown(f'# Generator: {name}')
    ui.markdown(f'This is the {name} generator page.')
```

**main.py**

```python
import api_pages
from nicegui import app, ui


@ui.page('/')
def index():
    ui.markdown('# Home')
    ui.link('Go to Tools', '/tools/')


app.include_router(api_pages.router)

ui.run(title='APIRouter Example')
```

**Verifica**: Ejecuta la app. Visita `/tools/` para confirmar que carga el índice de herramientas. Visita `/tools/generator/title` para confirmar que el parámetro de ruta dinámico funciona (deberías ver "Generator: title").

<Notice type="warning" title="Limitación de NiceGUI On Air">
APIRouter no funciona actualmente con NiceGUI On Air (el servicio de despliegue alojado). Usa el Enfoque 1 o 2 si planeas desplegar con On Air.
</Notice>

Para otro enfoque sobre apps Python multipágina, consulta nuestra guía para construir un [sitio web multipágina con FastHTML](/es/fasthtml-multiples-paginas/).

## ¿Qué enfoque deberías usar?

<Tabs>
<Tab name="App sencilla">
**Usa el Enfoque 1** (`@ui.page()` + frame) cuando tengas unas pocas páginas con layouts diferentes, o quieras que cada página sea una instancia independiente. Bueno para sitios públicos.
</Tab>
<Tab name="Dashboard / herramienta interna">
**Usa el Enfoque 2** (`ui.sub_pages`), la opción recomendada por defecto para la mayoría de los lectores. Navegación estilo SPA, shell persistente, transiciones rápidas del lado del cliente. Úsalo para dashboards, paneles de administración y herramientas internas.
</Tab>
<Tab name="App grande, muchas funcionalidades">
**Usa el Enfoque 3** (`APIRouter`) cuando tengas muchos grupos de funcionalidades, necesites prefijos de URL y quieras mantener los módulos limpiamente separados. Escala bien para desarrollo en equipo.
</Tab>
</Tabs>

## Verificación y solución de problemas de tus páginas NiceGUI

### Cómo verificar que todas las rutas están registradas

Después de arrancar la app:

```bash
python main.py
```

Comprueba lo siguiente:

1. Abre `http://localhost:8080`. La página de inicio debería cargar con el header y la navegación.
2. Haz clic en cada enlace del menú. Cada página debería renderizar su contenido dentro del frame compartido.
3. Revisa la terminal por errores de importación o advertencias
4. Confirma la versión de NiceGUI: `pip show nicegui` (debería ser ≥ 3.0.0)

### Errores comunes y soluciones

<Accordion label="RuntimeError: 'ui.page cannot be used in the global scope'" group="errors">
Mueve toda la creación de UI dentro de funciones `@ui.page()`. NiceGUI v3 eliminó el cliente compartido auto-index. Los elementos de UI en el ámbito global ya no están permitidos. Si tienes `ui.label('something')` en el nivel superior de un archivo, envuélvelo en una función de página.
</Accordion>

<Accordion label="ImportError en el módulo theme o menu" group="errors">
Ejecuta `python main.py` desde el directorio raíz del proyecto (donde viven `theme.py` y `menu.py`). Asegúrate de que `pages/__init__.py` existe (un archivo vacío es suficiente). Si usas una estructura anidada, revisa tu `PYTHONPATH`.
</Accordion>

<Accordion label="Las páginas muestran una página de error de 'cara triste'" group="errors">
Revisa la terminal para ver el traceback de Python. La función de página lanzó una excepción. Causas comunes: un import que falta, un error tipográfico en el nombre de un elemento, o referenciar una variable que no existe en el ámbito de la página.
</Accordion>

<Accordion label="Los enlaces de navegación no funcionan / 404" group="errors">
Verifica que las rutas coincidan exactamente, incluidas las barras finales. `/youtube-title-generator/` no es lo mismo que `/youtube-title-generator`. Las rutas en `ui.link()` deben coincidir con las rutas en `@ui.page()` o `ui.sub_pages()`.
</Accordion>

<Accordion label="response_timeout exceeded" group="errors">
Los constructores de página asíncronos que tardan más de 3 segundos necesitan un timeout mayor: `@ui.page('/', response_timeout=10)`. Esto ocurre cuando las páginas hacen cómputo pesado o llamadas de red durante la inicialización.
</Accordion>

<Accordion label="El layout se ve mal tras actualizar desde v1/v2" group="errors">
NiceGUI v3 incluye Tailwind 4 (actualizado desde Tailwind 3). Algunas clases de utilidad se comportan de forma diferente, en particular `line-height`, los valores por defecto de `border` y el espaciado. Consulta el changelog de Tailwind 4 si tu layout cambió tras la actualización.
</Accordion>

## Desplegando tu app NiceGUI multipágina

### Docker y proxy inverso

NiceGUI se ejecuta como un único worker de uvicorn: un proceso gestiona todas las conexiones de cliente. En producción, ponlo detrás de un proxy inverso (Nginx, Caddy o Traefik) y usa Docker para despliegues reproducibles.

La imagen oficial de Docker de NiceGUI es `zauberzeug/nicegui`. Para una guía completa sobre contenedorizar apps Python, consulta [desplegar apps Python con Docker](/es/docker-ejecutar-python/).

Ejemplo rápido de Docker Compose:

```yaml
services:
  nicegui:
    image: zauberzeug/nicegui:latest
    ports:
      - "8080:8080"
    volumes:
      - .:/app
    environment:
      - PORT=8080
      - HOST=0.0.0.0
```

NiceGUI v3.10+ lee correctamente `client.ip` detrás de proxies inversos. Asegúrate de que tu proxy envíe las cabeceras `X-Forwarded-For` y `X-Forwarded-Proto`.

### Notas de producción

Cosas clave que debes saber antes de ejecutar esto en producción:

- **Proceso worker único**: Todos los clientes comparten un proceso. Cada conexión `@ui.page()` mantiene su propio árbol de UI en memoria. Para apps de alto tráfico, escala horizontalmente con réplicas de Docker detrás de un balanceador de carga.
- **Estado compartido**: Cada cliente obtiene su propia instancia de UI. Usa `app.storage` para el estado por usuario o la clase `Event` (v3.0+) para comunicación entre clientes.
- **Autoalojamiento**: Para un VPS asequible, [Hetzner Cloud](https://go.bitdoze.com/hetzner) ofrece planes desde ~4 €/mes. [Hostinger VPS](https://go.bitdoze.com/hostinger-vps) es otra opción económica con virtualización KVM y almacenamiento NVMe.
- **Plataformas de despliegue**: Puedes autoalojar con [Dokploy](/es/instalar-dokploy/) o consultar los [mejores paneles de servidor autoalojados](/es/mejores-paneles-autoalojados/) para más opciones.

_Los precios de los VPS subieron en todos los proveedores en 2026 — si estás replanteándote una máquina alquilada, mira [qué cambió y cuándo gana un mini PC](/es/subidas-precios-vps/)._

## Consejos extra para NiceGUI v3

Algunas cosas que vale la pena saber y que no encajan en las secciones anteriores:

- **Navegación programática**: Usa `ui.navigate.to('/new-page')` — el antiguo `ui.open()` se eliminó en v3.
- **Props observables**: `.props()`, `.classes()` y `.style()` ya no necesitan llamadas a `.update()`. Son reactivos automáticamente en v3.
- **Tailwind 4**: NiceGUI v3 incluye Tailwind 4. Si tus clases se ven raras tras actualizar, consulta el changelog de Tailwind 4 por cambios importantes en line-height y valores por defecto de border.
- **Extensión de NiceGUI para VSCode**: Recomendada para el autocompletado de clases Tailwind. Reemplaza la API de Python `.tailwind` que se eliminó.
- **`ui.keep_alive()`**: Evita desconexiones de clientes inactivos (añadido en v3.11). Útil para sesiones de dashboard de larga duración.

<Accordion label="Checklist rápido de migración a v3" group="faq">
<ListCheck>
<ul>
  <li>Reemplaza `ui.open()` por `ui.navigate.to()`</li>
  <li>Elimina el uso de la API `.tailwind` — usa `.classes()` en su lugar</li>
  <li>Elimina las llamadas a `.update()` después de `.props()` / `.classes()` / `.style()`</li>
  <li>Asegura Python 3.10 o más reciente</li>
  <li>Fija `nicegui>=3.0.0` en requirements</li>
  <li>Mueve toda la UI dentro de funciones `@ui.page()` (sin elementos en el ámbito global)</li>
</ul>
</ListCheck>
</Accordion>

## Conclusión

Añadir múltiples páginas a NiceGUI se reduce a elegir el patrón adecuado para tu app:

- **`@ui.page()` + frame compartido** para sitios multipágina sencillos con layouts distintos
- **`ui.sub_pages`** para dashboards y herramientas internas con navegación consistente — esta es la opción recomendada por defecto para la mayoría de los proyectos nuevos
- **`APIRouter`** para apps grandes con muchos grupos de funcionalidades y desarrollo en equipo

El enfoque de `ui.sub_pages` es lo primero que usaría en cualquier proyecto nuevo de NiceGUI. La navegación del lado del cliente se siente más rápida, el shell persistente evita el parpadeo del layout, y el código es más simple — todo en un solo manejador `@ui.page('/')` con un diccionario de rutas a funciones.

Si estás construyendo herramientas con IA sobre NiceGUI, echa un vistazo a nuestra guía sobre [crear herramientas de IA con Python](/es/crear-agente-ia-mastra/). Para el contexto más amplio del ecosistema, consulta el resumen de los [mejores frameworks web de Python](/es/mejores-frameworks-web-python/).

<Button text="Documentación oficial de NiceGUI" link="https://nicegui.io/documentation" variant="solid" color="blue" size="md" icon="arrow-right" />

## Preguntas frecuentes

<Accordion label="¿Puedo usar NiceGUI con Tailwind CSS?" group="faq">
Sí. NiceGUI v3 incluye Tailwind 4. Usa `.classes('your-tailwind-classes')` en cualquier elemento. La antigua API de Python `.tailwind` (p. ej., `ui.element.tailwind.font_bold()`) se eliminó en v3 — usa el enfoque de `.classes()` con strings en su lugar.
</Accordion>

<Accordion label="¿Cómo comparto estado entre páginas?" group="faq">
En v3, cada `@ui.page()` crea una nueva instancia de cliente — el estado no se comparte entre usuarios por defecto. Usa `app.storage` para el estado persistente por usuario. Para comunicación entre clientes (p. ej., un dashboard que muestra datos de sensores en vivo a todos los usuarios), usa la clase `Event` introducida en v3.0.
</Accordion>

<Accordion label="¿Es NiceGUI adecuado para producción?" group="faq">
Sí, pero ejecútalo detrás de un proxy inverso (Nginx, Caddy o Traefik). NiceGUI es un proceso de worker único — un proceso uvicorn gestiona todas las conexiones de cliente. Para apps de alto tráfico, escala horizontalmente con múltiples réplicas de Docker detrás de un balanceador de carga. Cada conexión de cliente mantiene su propio árbol de UI, así que vigila el uso de memoria.
</Accordion>

<Accordion label="¿Cuál es la diferencia entre sub_pages y @ui.page()?" group="faq">
`@ui.page()` hace una recarga completa de página por ruta — el navegador solicita una nueva página al servidor. `ui.sub_pages` intercambia el contenido del lado del cliente dentro de un shell compartido (el header y la barra lateral permanecen fijos). Usa `sub_pages` para dashboards y apps con navegación consistente. Usa `@ui.page()` cuando las páginas necesiten layouts completamente diferentes o instancias de cliente independientes.
</Accordion>