st.fragment para reruns parciales y st.rerun/st.stop

Avanzado
Streamlit
Streamlit
Actualizado: 26/04/2026

El problema de los reruns completos

Por defecto, cualquier interacción con un widget provoca la re-ejecución de todo el script de arriba a abajo. En aplicaciones simples esto no supone un problema, pero en dashboards con carga de datos pesada, entrenamiento de modelos o consultas a APIs externas, cada rerun innecesario puede degradar significativamente la experiencia del usuario.

@st.fragment resuelve este problema permitiendo marcar funciones cuyo contenido se re-ejecuta de forma aislada, sin provocar el rerun del script completo. Cuando un usuario interactúa con un widget dentro de un fragmento, solo se re-ejecuta la función decorada, mientras que el resto del script permanece intacto:

flowchart TB
    Start[Inicio de interacción] --> Check{"Widget dentro<br/>de fragment?"}
    Check -->|No| Full[Rerun completo del script]
    Full --> F1[Carga datos pesados]
    Full --> F2[Renderiza chart]
    Full --> F3[Renderiza chat]
    Check -->|Si| Partial[Rerun solo del fragment]
    Partial --> F3P[Renderiza chat actualizado]
    F1 -.->|skipped| F3P
    F2 -.->|skipped| F3P
    Partial --> Every{"run_every=5s"}
    Every -->|Tick periódico| Partial
import streamlit as st
import time

# Sin fragment: cada clic en el botón del chat re-ejecuta TODO,
# incluyendo el gráfico pesado y la carga de datos

@st.fragment  # Solo se re-ejecuta este bloque cuando el usuario interactúa
def panel_chat():
    if "mensajes" not in st.session_state:
        st.session_state.mensajes = []

    for msg in st.session_state.mensajes:
        with st.chat_message(msg["rol"]):
            st.write(msg["texto"])

    if prompt := st.chat_input("Mensaje..."):
        st.session_state.mensajes.append({"rol": "user", "texto": prompt})
        st.session_state.mensajes.append({"rol": "assistant", "texto": f"Echo: {prompt}"})

# Este bloque se ejecuta UNA SOLA VEZ (no en cada mensaje del chat)
st.title("Dashboard con chat integrado")
with st.spinner("Cargando datos (operación costosa)..."):
    time.sleep(2)  # Simulación de carga pesada
    import pandas as pd
    import numpy as np
    df = pd.DataFrame(np.random.randn(100, 3), columns=["A", "B", "C"])

st.line_chart(df)  # Se renderiza una sola vez

panel_chat()  # Este bloque se re-ejecuta solo cuando el usuario escribe

@st.fragment con run_every: actualización automática

run_every hace que el fragmento se re-ejecute automáticamente cada N segundos o minutos:

import streamlit as st
import pandas as pd
import numpy as np
import time
from datetime import datetime

st.title("Monitor de sistema en tiempo real")

# Panel de métricas que se actualiza cada 2 segundos
@st.fragment(run_every="2s")
def metricas_tiempo_real():
    cpu = np.random.uniform(15, 95)
    mem = np.random.uniform(40, 85)
    req = np.random.randint(100, 500)

    col1, col2, col3 = st.columns(3)
    col1.metric("CPU", f"{cpu:.1f}%", f"{cpu - 50:.1f}%")
    col2.metric("Memoria", f"{mem:.1f}%", f"{mem - 60:.1f}%")
    col3.metric("Solicitudes/s", req, np.random.randint(-50, 50))
    st.caption(f"Última actualización: {datetime.now().strftime('%H:%M:%S')}")

# Panel de logs que se actualiza cada 5 segundos
@st.fragment(run_every="5s")
def logs_recientes():
    eventos = ["login", "logout", "error_404", "deploy", "backup"]
    evento = np.random.choice(eventos)
    usuario = f"user_{np.random.randint(1, 100)}"
    st.write(f"[{datetime.now().strftime('%H:%M:%S')}] {evento} — {usuario}")

metricas_tiempo_real()
st.divider()
st.subheader("Logs recientes")
logs_recientes()

Los valores de run_every aceptados:

  • String: "2s", "30s", "5m", "1h", "1d"
  • Número (segundos): 5 (equivale a "5s")

st.rerun: forzar una re-ejecución

st.rerun fuerza la re-ejecución completa del script desde el principio. Es una herramienta de control de flujo que se usa cuando se necesita que la interfaz refleje inmediatamente un cambio en session_state que se acaba de realizar, como después de un login exitoso, una actualización de datos o la eliminación de un registro:

import streamlit as st

if "autenticado" not in st.session_state:
    st.session_state.autenticado = False

if not st.session_state.autenticado:
    st.title("Iniciar sesión")
    with st.form("login"):
        usuario = st.text_input("Usuario")
        password = st.text_input("Contraseña", type="password")
        if st.form_submit_button("Entrar"):
            if usuario == "admin" and password == "1234":
                st.session_state.autenticado = True
                st.session_state.usuario = usuario
                st.rerun()  # Re-ejecutar para mostrar el dashboard
            else:
                st.error("Credenciales incorrectas.")
    st.stop()  # Detener aquí si no está autenticado

# Si llegamos aquí, el usuario está autenticado
st.title(f"Bienvenido, {st.session_state.usuario}!")
st.write("Contenido del dashboard protegido.")

if st.button("Cerrar sesión"):
    st.session_state.autenticado = False
    st.rerun()

st.stop: detener la ejecución del script

st.stop detiene la ejecución del script en ese punto. Todo el código posterior no se ejecuta. Es útil para flujos de autenticación, validación previa y gating de acceso:

import streamlit as st

st.title("Herramienta de análisis")

# Guard: requerir autenticación
if not st.session_state.get("autenticado"):
    st.warning("Debes iniciar sesión para acceder a esta herramienta.")
    st.page_link("pages/login.py", label="Ir a inicio de sesión")
    st.stop()  # Todo lo de abajo no se ejecuta

# Guard: requerir datos cargados
archivo = st.file_uploader("Sube un dataset CSV")
if archivo is None:
    st.info("Sube un archivo CSV para comenzar el análisis.")
    st.stop()

import pandas as pd
df = pd.read_csv(archivo)

# Guard: validar el DataFrame
if df.empty:
    st.error("El archivo está vacío.")
    st.stop()

if df.shape[0] < 10:
    st.warning(f"Solo hay {df.shape[0]} filas. Se necesitan al menos 10 para el análisis.")
    st.stop()

# Si llegamos aquí, todo es válido
st.success(f"Dataset cargado: {df.shape[0]} filas × {df.shape[1]} columnas.")
st.dataframe(df.head())

El patrón de st.stop como guard al principio del script es muy limpio para validar precondiciones antes de ejecutar el código principal. Cada st.stop actúa como una barrera que solo permite continuar si se cumple la condición necesaria, lo que evita la necesidad de anidar todo el código principal dentro de bloques if cada vez más profundos.

La diferencia clave entre st.rerun y st.stop es su intención: st.rerun provoca una nueva ejecución completa del script (desde la primera línea), mientras que st.stop detiene la ejecución en el punto exacto donde se invoca, sin iniciar un nuevo ciclo. Se complementan: st.stop para bloquear y st.rerun para reiniciar.

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

Definir fragmentos de UI que se re-ejecutan independientemente con @st.fragment. Usar run_every en st.fragment para actualizaciones automáticas periódicas. Controlar el flujo del script con st.rerun para forzar una re-ejecución. Detener la ejecución del script condicionalmente con st.stop. Aplicar st.fragment para paneles de tiempo real sin bloquear el resto de la UI.