streamlit: Make sure that clicking on a download button does not reload the entire app

Problem

The Streamlit app reloads entirely when clicking on a st.download_button. In my case, I think it should not: download buttons are different from buttons and should not trigger a reload of the app. They should only trigger a download.

I perfectly understand that a click on a button does reload the app (to recompute) but downloads don’t change the view, downloads don’t trigger computations etc.

I also know about the cache, but it is not always a proper solution: what about apps with a st.forms? The user should not have to click on yet another button to generate the same view that has not changed one bit. The same goes if several files can be downloaded: it can be very cumbersome to do such a simple task.

Solution

Several options:

  1. Stop reloading the app for a st.download_button
  2. Add a parameter to trigger (or not) a reload of the page when clicking on a st.download_button.

Additional context

See this discourse: https://discuss.streamlit.io/t/stop-page-from-reloading-after-using-downloader/18744

And this code to reproduce the problem exposed above:

import streamlit as st

def long_computation():
    import time
    time.sleep(10)

st.header("TEST DOWNLOAD BUTTON")

with st.spinner("Running a long computation.."):
    long_computation()

st.download_button("Download", "a")

Community voting on feature requests enables the Streamlit team to understand which features are most important to our users.

If you’d like the Streamlit team to prioritize this feature request, please use the 👍 (thumbs up emoji) reaction in response to the initial post.

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 135
  • Comments: 48

Most upvoted comments

Hey all! Just wanted to give a quick update: we are not actively working on this at the moment but it’s something we definitely want to do. The complication is that we probably need to find a solution that controls reruns across all interactive widget. Don’t want to do a one-off thing for download button and then have to revamp it once we want to give more control over reruns in other elements. I’ll update here once we have more news!

Update

We’ve been looking into this for the past few weeks. We probably just want to change the behavior of st.download_button to not rerun the app anymore, so it works similar to the new st.link_button. Unfortunately, that’s a breaking change, so we’d have to wait until Streamlit 2.0 to do it. We’re thinking about introducing a config option in the meantime that would disable the rerun, so y’all are unblocked for now. Will update once we reached a final decision!

@VolgardTheScientist Correct, this will not affect st.button.

It is in the roadmap now.

Following, I have faced this issue and would love a fix 😃

The same goes if several files can be downloaded

I have three files available for download on my Hugging Face App and it’s frustrating for the end user to have to run the application three times to be able to actually download each of those files.

+1 After initial enthusiasm, I went back to css styled hrefs in all of my use cases due to this.

Hi @jrieke - is there any progress on this? This functionality would be extremely useful. Thanks!

@GitHunter0 @jenitivecase Oh in case that wasn’t clear from my answer, we are only planning to change the rerun behavior of st.download_button, not of other widgets. Updated the wording in my answer to make that more obvious.

Hey all! Just wanted to give a quick update: we are not actively working on this at the moment but it’s something we definitely want to do. The complication is that we probably need to find a solution that controls reruns across all interactive widget. Don’t want to do a one-off thing for download button and then have to revamp it once we want to give more control over reruns in other elements. I’ll update here once we have more news!

Great to know @jrieke , that will be a major contribution since this is the biggest drawback of Streamlit in my opinion. I’m cheering for it.

+1 I understand that the Streamlit team has their design principles but reloading the app after clicking on download makes it unusable for me. The same applies to other widgets as well.

How about semi-solution heavily based on this:

import base64
import time
import streamlit as st
import pandas as pd


def long_computation():
    time.sleep(15)
    return pd.Series(range(1000), name='number')


@st.cache
def convert_csv(data):
    return data.to_csv(index=False).encode('utf-8')


def create_download_link(val, filename):
    b64 = base64.b64encode(val)
    return f'<a href="data:application/octet-stream;base64,{b64.decode()}" download="{filename}.csv">Download file</a>'


st.write(' ... doing long computation ...')

res = long_computation()

st.success('Long computation done!')

csv = convert_csv(res)

download_url = create_download_link(csv, 'test')

st.markdown(download_url, unsafe_allow_html=True)

I tried Streamlit for the first time today. Man I was surprised how good it is. My app takes input and generates two downloadable files, a certificate and a key. CRASH BOOM. I can only download one file, because the page reloads… I guess I have to look elsewhere. A pitty .

+1 This is a top priority, Streamlit team. This is a serious flaw in Streamlit design that prevents many developers from adopting Streamlit for large scale projects.

+1 - last blocker for Streamlit vs. R Shiny here.