Support `bind="query-params"` in `st.expander`, `st.tabs`, and `st.popover`
Author: JosephMarinierCreated Jun 5, 2026Updated Sep 16, 2026
Labelstype:enhancementpapercutfeature:query-paramsfeature:st.expanderfeature:st.popover
Checklist
- I have searched the existing issues for similar feature requests.
- I added a descriptive title and summary to this issue.
Summary
Add support for bind="query-params" in st.expander and st.popover.
Why?
I love both the new bind="query-params" feature, as well as the new "dynamic" st.expander (#13888), st.tabs (#13910), and st.popover (#13914) feature, and I'd like to use them together.
How?
Support bind="query-params" in st.expander(), st.tabs(), and st.popover().
Additional Context
As a workaround for st.tabs(bind="query-params"), I have been using st.segmented_control(bind="query-params"), but it's not as nice visually.
For a while now, in my app, I hacked together a dynamic expander + query param, using a st.button. It works, but it's messy.
from typing import Callable
import streamlit as st
def set_bool_query_param(key: str, value: bool):
"""Set a boolean query parameter in the URL."""
if value:
st.query_params[key] = value
else:
st.query_params.pop(key, None)
@st.fragment
def dynamic_expander(
label: str,
content: Callable,
*,
key: str,
help: str | None = None, # noqa: A002 Shadowing a Python builtin
):
"""Create an expander that only generates the content if it is expanded.
Unlike `st.expander()`, which is a frontend component that always generates the content,
this `dynamic_expander()` only generates the content if it is expanded.
"""
expanded = key in st.query_params
button = {
"label": label,
"help": help,
"type": "tertiary",
"icon": f":material/expand_{'less' if expanded else 'more'}:",
}
with st.container(border=True, key=key):
placeholder = st.empty()
if expanded:
placeholder.button(key=f"{key}_button_disabled", disabled=True, **button)
try:
with st.spinner("Loading..."):
content()
except Exception as e:
st.exception(e)
placeholder.button(
key=f"{key}_button",
on_click=set_bool_query_param,
args=(key, not expanded),
**button,
)
def example():
st.write("Content")
dynamic_expander("Example", example, key="example")
Source: streamlit/streamlit