Create a file in memory + start downloading upon clicking a button

I encountered the following situation:
My app is performing some calculations and displaying multiple plots at the same time. There are a couple of input widgets in the sidebar, which influence how the plots look like. So every time one of those input widgets is changed, the plots get rerendered.

Now, I would like to offer a button to download all of the plots currently shown as a zip file. Currently I implemented this using st.download_button and passing the zipfile data as data parameter (the zip file is created in memory using BytesIO).
It is working, however the problem is, that everytime an input widget changes and the plots change, the zipfile will be recreated which slows down the user experience - even though most of the time the user will not actually download the file.

I am rather looking for a solution to not only start the download by clicking a button, but also triggering the creation of the zip file only upon clicking the button.
How would you go about implementing this? I guess you’d need some kind of javascript?

Dummy code:

def figs2zip(figs) -> bytes:
    # create zip file containing all figs in memory using io.BytesIO()
    # return zip file buffer
    return zip_buf

# some input widgets
# create multiple plots using matplotlib and display them
for fig in figs:

# => this is slow, because figs2zip() will run on EVERY script rerun and
# always create a new zip file in memory
st.download_button(label='Download plots', data = figs2zip(fig), filename='',  mime='application/zip')

I would much rather want a behavior like this:

if st.button('Download plots'):
    zip_buf = figs2zip(fig)
    trigger_download(zip_buf)    # <--- How do you implement this? 🤨

I.e, the zip file gets only created when actually clicking the button, rather than on every script run. At the same time, after creating the file in memory the download dialogue should pop-up just like with st.download_button.


This would also be useful for me. Are there any updates on this?

Unfortunately not. Actually, I rephrased this question into and opened a new thread: Create + Download file upon clicking a button
There is some more discussion there, but not really any solution.

However, if you check github you’ll find that this issue has been mentioned multiple times in the past few months and there are quite some upvotes. Also a few streamlit devs are involved in the discussion on github. I hope the streamlit team will implement some changes to resolve this issue.

Thanks for the info @Wally!

FYI. This is the most discussed issue in GitHub regarding this topic: Make sure that clicking on a download button does not reload the entire app · Issue #4382 · streamlit/streamlit · GitHub
Maybe you could leave an upvote as well. :slight_smile:

Another one: Defered data for download button · Issue #5053 · streamlit/streamlit · GitHub
And this one: `st.download_button`: Create downloadable data only when button is clicked. · Issue #5899 · streamlit/streamlit · GitHub

1 Like

Check the dev roadmap for streamlit at:

They finally added this to the Feb-Apr 2023 timeline:

:stopwatch: Deferred data load for st.download_button
st.download_button accepts a function, which is only executed once it’s clicked.

:sunglasses: Looks like this issue is going to get resolved soon.

1 Like