Download Buttons Disappear After Clicking in Streamlit App

Hello Streamlit Community,
I’m developing a Streamlit application for E-mobility profile generation. After I click the “Generate profiles” button, the app displays several download buttons for different output files (e.g., Timeseries Data, JSON Output, Visualization Data, All Outputs).

The problem: When I click on one of these download buttons to download a file, the other download buttons disappear from the app. To download another file, I have to click “Generate profiles” again, which is not ideal for user experience.

My Question:
How can I ensure the download buttons and outputs persist across reruns without needing to click “Generate Profiles” again? Any insights or alternative approaches would be greatly appreciated!

Thank you for your help! :pray:

Alternative to what? You didn’t tell much about your current approach. Maybe you are doing something like this:

if st.button("Generate Profiles"):
    generate_profiles()
    st.download_button("Download Timeseries", ts_data)
    st.download_button("Download Json", json_data)

You only want to generate the profiles once but you may want to click on several download buttons. So the conditions to generate the profiles and showing the download buttons should be different. You need something like this:

if st.button("Generate Profiles"):
    generate_profiles()

if ready_to_download():
    st.download_button("Download Timeseries", profiles.ts_data)
    st.download_button("Download Json", profiles.json_data)

How you decide whether the data is ready to download or not depends on how your code works.

Thanks for your reply @Goyo .
This is my current approach:

def emobility():
“”“Handle the interactive exploration simulation of e-mobility profiles.”“”
st.set_page_config(page_title=“mm.esd data explorer”, layout=“wide”)
initialize_session_state()
session_key = st.session_state.emobility_page_data

display_title()
handle_json_upload(session_key)
emobility_data_obj = get_emobility_data(session_key)
provide_download_options(emobility_data_obj)

if st.button("Generate profiles", type="primary"):
    generate_profiles(
        emobility_data_obj,
        session_key,
    )

visualize_profiles(emobility_data_obj)

def initialize_session_state():
“”“Initialize session state variables.”“”
if “emobility_page_data” not in st.session_state:
st.session_state.emobility_page_data = {
“json_mode”: False,
“input_data_emobility”: None,
“first_run”: True,
“timeseries_profile”: None,
“output_data”: None,
“timeseries_visualization”: None,
“all_data”: None,
}

def display_title():
“”“Display the page title.”“”
st.title(“Emobility SLP”)

def get_emobility_data(session_key):
“”“Retrieve e-mobility data based on the input format.”“”
input_format = (
“json”
if session_key[“json_mode”] and session_key[“input_data_emobility”]
else “UI”
)
emobility_data_obj = (
DataInputEmobility(
input_mode=input_format,
default_values=session_key[“input_data_emobility”],
)
if input_format == “json”
else DataInputEmobility(input_mode=input_format)
)
emobility_input_data = emobility_data_obj.run_input()
return emobility_input_data

def provide_download_options(all_input_data):
“”“Provide download options for the input data.”“”
input_data_json = all_input_data.json()
st.download_button(
label=“Save (.json)”,
data=json.dumps(input_data_json, indent=4),
file_name=“input_data_json_emobility.json”,
mime=“application/json”,
)

def handle_json_upload(session_key):
“”“Handle uploading a JSON file for input data.”“”
formbtn = st.button(“Load (.json)”)

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

if formbtn or st.session_state.formbtn_state:
    st.session_state.formbtn_state = True
    with st.form(clear_on_submit=True, key="upload_json_file"):
        uploaded_file = st.file_uploader(
            "Load your json file",
            type="json",
            accept_multiple_files=False,
        )
        if st.form_submit_button("Submit"):
            if uploaded_file is not None:
                st.success("File uploaded successfully")
                try:
                    uploaded_json = json.load(uploaded_file)
                    validation_obj = ValidationDataEmobility()
                    valid_data = validation_obj.validation_data(
                        uploaded_json
                    )
                    session_key["input_data_emobility"] = valid_data
                    session_key["json_mode"] = True
                    st.rerun()

                except PydanticValidation as e:
                    st.exception(e)
                    session_key["input_data_emobility"] = None
            else:
                st.error("Please upload a json file")
                session_key["input_data_emobility"] = None

def generate_profiles(
emobility_data_obj,
session_key,
):
“”“Generate profiles and provide download options.”“”
profile_obj = ProfileGeneratorEmobility(emobility_data_obj)
df, profile = profile_obj.generate_load_profiles()
session_key[“timeseries_profile”] = df
session_key[“timeseries_profile_visualization”] = profile
session_key[
“output_data”
] = profile_obj.list_pydantic_charger_data.model_dump()
(
time_series_column,
timeseries_visu_column,
json_column,
all_output_column,
) = st.columns(4)
if session_key[“timeseries_profile”] is not None:
with time_series_column:
provide_download_button(
“Timeseries Data”,
session_key[“timeseries_profile”].to_csv(index=True, sep=“,”),
“timeseries_profile.csv”,
)

if session_key["output_data"] is not None:
    output_data_json = {
        "Input data": json.loads(emobility_data_obj.json()),
        "Output data": session_key["output_data"],
    }

with json_column:
    provide_download_button(
        "JSON Output",
        json.dumps(output_data_json, indent=4),
        "output_data_json_emobility.json",
    )

if session_key["timeseries_profile_visualization"] is not None:
    with timeseries_visu_column:
        provide_download_button(
            "Visualization Data",
            session_key["timeseries_profile_visualization"].value.to_csv(
                index=False,
            ),
            "visualization_timeseries_profile.csv",
        )
with all_output_column:
    prepare_combined_output(session_key, emobility_data_obj)

def prepare_combined_output(session_key, all_input_data):
“”“Prepare combined CSV, JSON outputs, and add input JSON to the zip file.”“”
df1 = session_key[“timeseries_profile”]
df2 = session_key[“timeseries_profile_visualization”].value
df2.drop(df2.columns[[0]], axis=1, inplace=True)
df1.index.name = “Date”
df2 = df2.set_index(df1.index)
combined_df_csv = pd.concat([df1, df2], axis=1).to_csv(index=True, sep=“,”)
out_put_json = json.dumps(session_key[“output_data”], indent=4)
input_data_json = all_input_data.json()

with open("combined_df.csv", "w", newline="", encoding="utf-8") as f:
    f.write(combined_df_csv)
with open("output_data.json", "w", encoding="utf-8") as f:
    f.write(out_put_json)

with open("input_data.json", "w", encoding="utf-8") as f:
    f.write(input_data_json)

with zipfile.ZipFile("data.zip", "w") as zipf:
    zipf.write("combined_df.csv")
    zipf.write("output_data.json")
    zipf.write("input_data.json") 

with open("data.zip", "rb") as f:
    session_key["all_data"] = f.read()

if session_key["all_data"] is not None:
    provide_download_button(
        "All Outputs",
        session_key["all_data"],
        "data.zip",
        mime="application/zip",
    )

def provide_download_button(label, data, file_name, mime=“application/json”):
“”“Help function to provide a download button.”“”
st.download_button(
label=label,
data=data,
file_name=file_name,
mime=mime,
)

def visualize_profiles(emobility_data_obj):
“”“Visualize the profiles.”“”
emobility_obj = ProfileSimulation(sector=“emobility”)
emobility_obj.profile_visualizer(emobility_data_obj)

if name == “main”:
emobility()

You have generate_profiles which is not only generating the profiles, but also showing the download buttons. As I said, you need to separate profile generation from showing the download buttons, because you want them happen at different times for different reasons.