Button inside st.expander to refresh only info in specific expander


I have multiple st.expanders and inside each one I want to have a button that will then rerun code that’s related to that expander (without rerunning everything else), and then update the text written in the expander.

I’ve tried using st.session_state in multiple ways to achieve this behavior, but I couldn’t figure out how to do it. I would appreciate your help!

Steps to reproduce

Code snippet:

import streamlit as st
from dataclasses import dataclass

class ExampleClass:
    name: str
    text: str

    def __int__(self, name, text):
        self.name = name
        self.text = text

    def edit_text(self):
        Runs external logic that modifies the self.text field
        self.text = self.text + ' modified text!'

def write_example_class(example_class: ExampleClass):

def create_streamlit_app():

    st.title("My title")

    if st.button("Run program"):
        # Here I have other code that creates two lists of ExampleClass
        # objects - relevant ones and not relevant ones.
        # For this example I just created the list of objects manually

        relevant_1 = ExampleClass('name1', 'relevant1')
        relevant_2 = ExampleClass('name2', 'relevant2')
        relevant_3 = ExampleClass('name3', 'relevant3')

        not_relevant_1 = ExampleClass('name1', 'not_relevant1')
        not_relevant_2 = ExampleClass('name2', 'not_relevant2')
        not_relevant_3 = ExampleClass('name3', 'not_relevant3')

        relevant_example_class = [relevant_1, relevant_2, relevant_3]
        not_relevant_example_class = [not_relevant_1, not_relevant_2, not_relevant_3]

        # continuation of real code
        st.header(f"Relevant examples ")

        for i, example_class in enumerate(relevant_example_class,
            expander_text = rf""" **{example_class.name}**"""

            example_class.edit_text() #run some code on the example_class
            # object which modifies the "text" field
            with st.expander(expander_text):
                write_example_class(example_class) # writes the modified text

        st.header(f"Not relevant examples:")

        for i, example_class in enumerate(not_relevant_example_class,
            expander_text = rf""" **{example_class.name}**"""
            with st.expander(expander_text):

                # here I want to give the user an option to run the
                # edit_text function on the not relevant examples
                if st.button('modify text', key=i):

                    # but this here doesn't work, because when button is
                    # clicked, the script restarts from the beginning

if __name__ == '__main__':

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

Expected behavior:

When the user clicks the button “modify text” inside the not_relevant_ expander, the text in the not_relevant_ expander will update to say “not_relevant modified text!”
For example, if the user clicks the “modify text” inside the not_relevant_1 expander, the text there will change to be “not_relevant1 modified text!”

Actual behavior:
When I click the “modify text” button, the entire page refreshes and brings me back to the original state where all I see is the title and the “Run program” button.

I’ve tried adding a session state variable to each button, but it doesn’t seem to work. What is the correct code design to use for getting this behavior?

Debug info

  • Streamlit version: 1.26.0
  • Python version: 3.11.5
  • Using pip
  • OS version: Windows
  • Browser version: Chrome

Requirements file

My requirements.txt file:

Thank you very much!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.