st.tabs, st.expander y st.popover para contenido organizado

Básico
Streamlit
Streamlit
Actualizado: 26/04/2026

st.tabs: navegación por pestañas

st.tabs divide el contenido en pestañas horizontales que el usuario puede seleccionar. Es ideal para separar vistas del mismo dato (resumen, detalle, exportar):

import streamlit as st
import pandas as pd
import numpy as np

st.title("Análisis de ventas")

tab_resumen, tab_detalle, tab_grafico, tab_exportar = st.tabs([
    "📊 Resumen",
    "📋 Detalle",
    "📈 Gráfico",
    "💾 Exportar"
])

df = pd.DataFrame({
    "mes": range(1, 13),
    "ventas": np.random.randint(30000, 60000, 12),
    "costes": np.random.randint(20000, 40000, 12)
})
df["beneficio"] = df["ventas"] - df["costes"]

with tab_resumen:
    col1, col2, col3 = st.columns(3)
    col1.metric("Total ventas", f"€ {df['ventas'].sum():,.0f}")
    col2.metric("Total costes", f"€ {df['costes'].sum():,.0f}")
    col3.metric("Beneficio neto", f"€ {df['beneficio'].sum():,.0f}")

with tab_detalle:
    st.dataframe(df, use_container_width=True, hide_index=True)

with tab_grafico:
    st.line_chart(df, x="mes", y=["ventas", "costes", "beneficio"])

with tab_exportar:
    csv = df.to_csv(index=False).encode("utf-8")
    st.download_button("Descargar CSV", csv, "ventas_2026.csv", "text/csv", icon="📥")
flowchart TD
    A[Mucho contenido en una página] --> B{"Cómo organizar?"}
    B -->|Vistas hermanas| C[st.tabs lista títulos]
    B -->|Detalle ocultable| D[st.expander label]
    B -->|Info contextual| E[st.popover label]
    C --> F[Tab1 Tab2 Tab3 con contenido propio]
    D --> G[Plegable abre con click]
    E --> H[Popover flotante temporal]
    F --> I[Mismo dato vistas distintas]
    G --> J[Detalles técnicos opcionales]
    H --> K[Ayuda inline sin scroll]
    I --> L[Reduce scroll vertical]
    J --> L
    K --> L

Tabs con iconos y emojis

import streamlit as st

tab_dashboard, tab_config, tab_ayuda = st.tabs(["Dashboard", "⚙️ Configuración", "❓ Ayuda"])

with tab_dashboard:
    st.write("Contenido principal del dashboard")

with tab_config:
    st.write("Configuración de parámetros")
    umbral = st.slider("Umbral de alerta", 0, 100, 75)
    frecuencia = st.selectbox("Frecuencia de actualización", ["Diaria", "Semanal", "Mensual"])

with tab_ayuda:
    st.markdown("""
    ## Cómo usar este dashboard

    1. Navega entre las pestañas para ver diferentes vistas.
    2. Configura los parámetros en la pestaña **Configuración**.
    3. Exporta los datos desde la pestaña **Exportar**.

    ¿Tienes dudas? Contacta con soporte@empresa.com
    """)

st.expander: secciones colapsables

st.expander oculta contenido que el usuario puede expandir a demanda. Reduce el scroll vertical y mantiene la interfaz limpia:

import streamlit as st

st.title("Explorador de modelos ML")
st.write("Selecciona el modelo y configura los hiperparámetros para entrenar.")

# Configuración básica siempre visible
modelo = st.selectbox("Modelo", ["Random Forest", "XGBoost", "SVM"])

# Parámetros avanzados colapsados por defecto
with st.expander("Hiperparámetros avanzados", expanded=False):
    st.write("Ajusta estos parámetros solo si tienes experiencia con el modelo seleccionado.")
    if modelo == "Random Forest":
        n_est = st.slider("n_estimators", 10, 500, 100)
        max_depth = st.slider("max_depth", 1, 50, 10)
        min_samples = st.slider("min_samples_split", 2, 20, 2)
    elif modelo == "XGBoost":
        lr = st.number_input("learning_rate", 0.001, 1.0, 0.1, format="%.3f")
        n_rounds = st.slider("n_estimators", 50, 1000, 200, step=50)

# Información técnica colapsada
with st.expander("ℹ️ Descripción del algoritmo"):
    descripciones = {
        "Random Forest": "Ensemble de árboles de decisión que reduce la varianza mediante bagging.",
        "XGBoost": "Gradient boosting optimizado con regularización L1/L2 y manejo eficiente de datos dispersos.",
        "SVM": "Clasificador de margen máximo que proyecta los datos a espacios de alta dimensión."
    }
    st.write(descripciones[modelo])

st.expander con expanded=True

import streamlit as st

# Expandido por defecto para contenido relevante
with st.expander("Resultados del modelo (click para colapsar)", expanded=True):
    st.success("Entrenamiento completado exitosamente.")
    col1, col2, col3 = st.columns(3)
    col1.metric("Accuracy", "94,3%")
    col2.metric("Precision", "93,7%")
    col3.metric("Recall", "95,1%")

st.popover: información contextual

st.popover muestra un panel flotante al hacer clic en un botón. Ideal para información de ayuda contextual o formularios secundarios:

import streamlit as st

col1, col2 = st.columns([3, 1])

with col1:
    st.write("Análisis de componentes principales (PCA)")
    n_componentes = st.slider("Número de componentes", 2, 10, 3)

with col2:
    with st.popover("ℹ️ Qué es PCA"):
        st.markdown("""
        **PCA** (Principal Component Analysis) es una técnica de reducción de dimensionalidad
        que transforma los datos en un nuevo sistema de coordenadas donde:

        - Los ejes representan las **direcciones de máxima varianza**
        - Los primeros componentes explican la mayor parte de la información
        - Permite visualizar datos de alta dimensión en 2D o 3D

        *Regla práctica*: Elige el número de componentes que explique al menos el 90% de la varianza.
        """)

# Popover con formulario
with st.popover("Filtros avanzados"):
    precio_min = st.number_input("Precio mínimo", value=0)
    precio_max = st.number_input("Precio máximo", value=1000)
    solo_activos = st.checkbox("Solo registros activos", value=True)
    if st.button("Aplicar", type="primary"):
        st.success("Filtros aplicados")

Combinación de tabs, expanders y columnas

import streamlit as st
import pandas as pd
import numpy as np

st.title("Dashboard de monitoreo de modelos ML")

tab_metricas, tab_logs, tab_config = st.tabs(["Métricas", "Logs", "Configuración"])

with tab_metricas:
    col1, col2 = st.columns(2)

    with col1:
        st.subheader("Modelo en producción")
        st.metric("AUC-ROC", "0,9431", "+0,0215")
        st.metric("Latencia media", "45 ms", "-3 ms")

        with st.expander("Métricas detalladas"):
            df_metricas = pd.DataFrame({
                "Métrica": ["Precision", "Recall", "F1-Score", "AUPR"],
                "Valor": [0.9337, 0.9512, 0.9424, 0.9218]
            })
            st.dataframe(df_metricas, hide_index=True)

    with col2:
        st.subheader("Evolución reciente")
        df_ev = pd.DataFrame({"día": range(30), "auc": 0.92 + np.cumsum(np.random.randn(30) * 0.002)})
        st.line_chart(df_ev, x="día", y="auc")

Contexto: cuándo usar tabs, expanders o popovers

Los tres componentes resuelven el mismo problema (ocultar contenido hasta que el usuario lo necesite) pero con matices muy distintos. st.tabs es la opción más visible y persistente: el usuario ve todas las pestañas a la vez y sabe cuántas secciones existen. Es perfecto para vistas alternativas del mismo dato (resumen, detalle, gráfico, exportación) donde cada pestaña representa un modo de consumo diferente.

st.expander es más discreto: el contenido queda oculto tras un título y solo se expande bajo demanda. Úsalo para información secundaria (detalles técnicos, hiperparámetros avanzados, notas de ayuda) que no todos los usuarios necesitan ver. Es también la forma recomendada de mostrar trazas de error o logs detallados sin saturar la interfaz.

st.popover es un overlay flotante: aparece como una capa encima del contenido y se cierra al hacer clic fuera. Es útil para formularios breves, filtros puntuales o explicaciones contextuales donde no quieres desplazar el contenido de la página. A diferencia del expander, el popover no empuja los elementos hacia abajo.

Explicación línea por línea del ejemplo de dashboard

En el bloque integrado final se combinan pestañas, columnas y expanders para construir un monitor de modelos de producción. Repasemos el flujo:

  1. tab_metricas, tab_logs, tab_config = st.tabs([...]) crea tres contenedores independientes; Streamlit los renderiza todos en memoria pero solo muestra uno a la vez.
  2. Dentro de tab_metricas se subdivide la pestaña con st.columns(2) para aprovechar el espacio horizontal.
  3. La columna izquierda muestra métricas KPI con st.metric y anida un st.expander para detalles que el analista puede desplegar si quiere profundizar.
  4. La columna derecha muestra una evolución temporal con st.line_chart, que es lo suficientemente compacto como para caber al lado de las métricas.

Tabla de parámetros

| Componente | Parámetro | Descripción | |-----------|-----------|-------------| | st.tabs | tabs | Lista de strings con los títulos de las pestañas | | st.expander | label | Texto visible cuando el expander está cerrado | | st.expander | expanded | Si True, arranca abierto (por defecto False) | | st.expander | icon | Icono opcional (emoji o Material) | | st.popover | label | Texto del botón que abre el popover | | st.popover | help | Tooltip al pasar el ratón sobre el botón | | st.popover | disabled | Desactiva el popover (no se puede abrir) | | st.popover | use_container_width | Ajusta el ancho del botón al contenedor |

Errores comunes

Crear tabs dentro de tabs de forma excesiva. Streamlit permite anidar pestañas, pero a partir de dos niveles la experiencia de usuario empeora mucho. Si necesitas más de dos niveles de jerarquía, reconsidera si el contenido se beneficiaría de un st.navigation multipágina.

Expanders dentro de sidebars muy estrechos. El contenido de un expander hereda el ancho de su contenedor. Si lo colocas en la sidebar y la sidebar es estrecha, gráficos o tablas pueden verse comprimidos. En esos casos, es mejor mover el contenido al área principal.

Contenido pesado en cada tab. Aunque solo ves una pestaña activa, Streamlit ejecuta todo el código de todas las pestañas en cada rerun. Si una pestaña carga un modelo pesado, ralentizará toda la app. Usa @st.cache_data o @st.cache_resource para que la carga solo ocurra una vez.

Popovers con formularios grandes. Los popovers están pensados para interacciones breves. Si necesitas un formulario con muchos campos, es mejor usar st.dialog (modal) o una pestaña dedicada.

Mejores prácticas

  • Limita a 4-6 pestañas por st.tabs: más allá de eso, las pestañas se apilan y pierden legibilidad.
  • Usa expanded=True con moderación: si todos los expanders arrancan abiertos, pierdes el beneficio de ocultar contenido.
  • Para contenido de ayuda breve (una frase o dos), prefiere el parámetro help de los widgets antes que un popover completo.
  • Combina st.tabs con st.container para aislar el estado visual de cada sección y evitar que widgets de una pestaña se reactiven al cambiar de pestaña.
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Streamlit es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Streamlit

Explora más contenido relacionado con Streamlit y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Crear pestañas de navegación horizontal con st.tabs para separar secciones. Usar st.expander para ocultar contenido secundario y detalles técnicos. Implementar st.popover para información contextual en ventanas emergentes. Combinar tabs con columnas y otros elementos de layout. Aplicar patrones de organización de contenido para reducir el scroll vertical.