Callbacks con on_change, on_click, st.query_params y st.context

Intermedio
Streamlit
Streamlit
Actualizado: 26/04/2026

Callbacks: on_change y on_click

Los callbacks son funciones que se ejecutan antes de que el script se re-ejecute cuando un widget cambia de valor. Este orden de ejecución es fundamental: primero se ejecuta el callback, después se re-ejecuta el script de arriba a abajo. Esto permite preparar el estado, validar datos o derivar valores calculados que estarán disponibles cuando el resto del script se ejecute.

Los callbacks se vinculan a los widgets mediante los parámetros on_change (para widgets que almacenan un valor) y on_click (para botones):

import streamlit as st

# Callback sin argumentos
def calcular_total():
    precio = st.session_state.get("precio", 0)
    cantidad = st.session_state.get("cantidad", 1)
    st.session_state.total = precio * cantidad

precio = st.number_input("Precio unitario (€)", value=10.0, key="precio", on_change=calcular_total)
cantidad = st.number_input("Cantidad", value=1, min_value=1, key="cantidad", on_change=calcular_total)

total = st.session_state.get("total", precio * cantidad)
st.metric("Total", f"€ {total:.2f}")

Un aspecto importante de los callbacks es que se ejecutan antes del rerun, no durante. Esto significa que los valores actualizados en session_state dentro del callback ya están disponibles cuando el script empieza a ejecutarse de nuevo, lo que permite inicializar la interfaz con datos derivados o calculados.

sequenceDiagram
    participant User as Usuario
    participant Widget as Widget Streamlit
    participant CB as Callback on_change
    participant Script as Script principal
    participant State as session_state
    User->>Widget: Cambia valor
    Widget->>CB: Ejecuta antes del rerun
    CB->>State: Actualiza cálculos derivados
    CB->>State: Valida o transforma
    Widget->>Script: Dispara rerun completo
    Script->>State: Lee estado calculado
    Script->>User: Renderiza UI nueva
    Note over CB,State: args y kwargs personalizan callback

Callbacks con argumentos: args y kwargs

Cuando el callback necesita saber qué widget lo invocó o recibir datos contextuales, se pueden pasar argumentos posicionales con args (tupla) y argumentos con nombre con kwargs (diccionario):

import streamlit as st

def actualizar_historial(nombre_widget, valor_nuevo=None):
    if "historial_cambios" not in st.session_state:
        st.session_state.historial_cambios = []
    valor = valor_nuevo or st.session_state.get(nombre_widget, "N/A")
    st.session_state.historial_cambios.append(f"{nombre_widget} → {valor}")

# args: tupla posicional
region = st.selectbox(
    "Región",
    ["Norte", "Sur", "Este", "Oeste"],
    key="region",
    on_change=actualizar_historial,
    args=("region",)
)

# kwargs: diccionario
año = st.slider(
    "Año",
    2020, 2026, 2025,
    key="año",
    on_change=actualizar_historial,
    kwargs={"nombre_widget": "año"}
)

with st.expander("Historial de cambios"):
    for cambio in st.session_state.get("historial_cambios", []):
        st.write(f"• {cambio}")

on_click en st.button

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

def cargar_datos(fuente):
    st.session_state.df = pd.DataFrame(
        np.random.randn(100, 3),
        columns=[f"Col_{i}" for i in range(1, 4)]
    )
    st.session_state.fuente_datos = fuente
    st.session_state.datos_cargados = True

# El callback se ejecuta cuando se hace clic, ANTES del rerun
st.button("Cargar datos de producción", on_click=cargar_datos, args=("produccion",))
st.button("Cargar datos de prueba", on_click=cargar_datos, args=("prueba",))

if st.session_state.get("datos_cargados"):
    st.success(f"Datos cargados desde: **{st.session_state.fuente_datos}**")
    st.dataframe(st.session_state.df.head())

st.query_params: parámetros de URL

st.query_params permite leer y modificar los parámetros de la query string de la URL, haciendo posible crear URLs compartibles que preservan el estado de los filtros de la aplicación. Cuando un usuario comparte la URL con los parámetros, el destinatario ve la misma vista filtrada al abrir el enlace:

import streamlit as st

# URL: http://localhost:8501/?region=Norte&año=2026

# Leer parámetros de la URL (con valores por defecto)
region = st.query_params.get("region", "Norte")
año = int(st.query_params.get("año", 2026))

st.title("Dashboard de ventas")

# Los widgets se inicializan con los valores de la URL
region_sel = st.selectbox("Región", ["Norte", "Sur", "Este", "Oeste"],
                           index=["Norte", "Sur", "Este", "Oeste"].index(region))
año_sel = st.slider("Año", 2020, 2026, año)

# Actualizar la URL cuando cambien los widgets
st.query_params["region"] = region_sel
st.query_params["año"] = str(año_sel)

st.write(f"Mostrando datos de **{region_sel}** en **{año_sel}**")
st.caption(f"URL compartible: ?region={region_sel}&año={año_sel}")

# Limpiar todos los parámetros
if st.button("Restablecer URL"):
    st.query_params.clear()
    st.rerun()

st.context: información del entorno del usuario

st.context proporciona acceso a información sobre el entorno del usuario que ejecuta la aplicación, como el tema visual activo y el idioma del navegador. Esta información permite adaptar dinámicamente la interfaz al contexto del usuario:

import streamlit as st

st.title("Información del contexto")

# Tema activo del usuario
tema = st.context.theme
st.write(f"Tema activo: **{tema}** (light/dark/auto)")

# Locale del usuario (idioma del navegador)
locale = st.context.locale
st.write(f"Locale del navegador: **{locale}**")

# Adaptar la app según el tema
if tema == "dark":
    color_grafico = "#FFFFFF"
else:
    color_grafico = "#000000"

st.write(f"Color del gráfico adaptado al tema: {color_grafico}")

Patrón completo: dashboard con estado en URL

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

# Leer estado de URL
defaults = {
    "año": "2026",
    "region": "Norte",
    "metrica": "ventas"
}
params = {k: st.query_params.get(k, v) for k, v in defaults.items()}

st.title("Dashboard compartible")
st.caption("Los filtros se guardan en la URL para compartir con el equipo.")

with st.sidebar:
    año = st.select_slider("Año", [2020, 2021, 2022, 2023, 2024, 2025, 2026],
                           value=int(params["año"]))
    region = st.selectbox("Región", ["Norte", "Sur", "Este", "Oeste"],
                          index=["Norte", "Sur", "Este", "Oeste"].index(params["region"]))
    metrica = st.radio("Métrica", ["ventas", "costes", "beneficio"],
                       index=["ventas", "costes", "beneficio"].index(params["metrica"]))

# Actualizar URL
st.query_params["año"] = str(año)
st.query_params["region"] = region
st.query_params["metrica"] = metrica

# Mostrar datos con los filtros aplicados
np.random.seed(int(año))
df = pd.DataFrame({"mes": range(1, 13), metrica: np.random.randint(5000, 15000, 12)})

st.subheader(f"{metrica.capitalize()} — {region} ({año})")
st.line_chart(df, x="mes", y=metrica)
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

Usar on_change para ejecutar funciones cuando cambia el valor de un widget. Implementar on_click en st.button para ejecutar acciones en el momento del clic. Pasar argumentos a los callbacks con los parámetros args y kwargs. Leer y modificar parámetros de la URL con st.query_params para URLs compartibles. Acceder al contexto del usuario (tema, dispositivo) con st.context.