Control state of `st.expander`

Summary

st.expander retains its state after interaction with another element.

Steps to reproduce

Code snippet:

import streamlit as st

button = st.button('Button')

with st.expander('expander', expanded=False):
    st.write('Hello!')

Launch and click expander, then click the button.

Expected behavior:

Expected the expander to unexpand after the button press, due to expanded=False.

Actual behavior:

The expander remains expanded (it does unexpand when the page is refreshed, though). I understand it often makes sense to have the expansion state persist automatically, but I can’t seem to find an easy way to control the state after the first initialization.

Debug info

  • Streamlit version: 1.16.0
  • Python version: 3.11.0
  • pyenv
  • OS version: MacOS 12.5.1
  • Browser version: Safari 16.0

You can achieve this behavior using st.session_state.

import streamlit as st

# initialise a boolean attr in session state

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

# write a function for toggle functionality
def toggle():
    if st.session_state.button:
        st.session_state.button = False
    else:
        st.session_state.button = True

# create the button
st.button("Button", on_click=toggle)

with st.expander('expander', expanded=st.session_state.button):
    st.write('Hello!')

Thanks! Unfortunately this doesn’t quite solve my problem… toggling the expanded state with the button does seem to work, but I want the button to be one-directional (click it, and it unexpands the expander if it is expanded, but not vice versa).

You can either modify the toggle function so it only sets st.session_state.button = True which would remove the reverse functionality, or you can disable or hide the button after you get your one and only click that you wish to track.