I wrote a multipage app where each page hosts a number of components which read data via API from an independent backend server.
The problem is when another page is requested, there are a few seconds when the old page begins to vanish (but not completely: it assumes a whitish color), and then, one by one, Streamlit components appear almost in random order to populate the new page.
The final effect is ugly to see, and the whole app looks very unprofessional.
Is there any way to apply a “loading blanket” (it could be a full screen image) to hide immediately the old page and that will be removed as soon as the new page has been fully prepared in background?
Or is it possible to modify st.Spinner using CSS to hide the whole page till it is created?
As an alternative, is there any JS event to know that the page has been completely created in Frontend, and its components are alive and kicking?
I have created a simple example app where you can position all the page elements to run after the time element (you can replace this with a lengthy calculation). Thus, the page elements will only be displayed only after the lengthy calculation is complete (in this case the time.sleep(10)).
Here’s the code:
import time
import streamlit as st
with st.spinner('Wait for it...'):
time.sleep(10)
st.title('Test app')
st.write('1')
st.write('2')
st.success('Done!')
Hi.
Alas I did a very similar test, but it doesn’t work in my case.
Just to show what I mean, I made an animated GIF to show the effect of clearing and adding all components again and again at every page change (with streamlit_option_menu), but it is too big to be seen here. Here is an external link.
The speed I very slow because I choose for this example a few components that are intrinsecally slow to refresh (each is a P5js sketch embbeded in the page, not a proper React component).
In any case, this slowness in rendering the page is visible also when there are a lot of standard Streamlit components.
So my question has to do with a procedural trick I would like to implement:
overlay a nice image full screen with an hourglass, a nice kitty or something similar just to distract the user for a few seconds;
clear the previous page content;
populate the page of the necessary components according to the active page, even if this takes a few seconds;
remove the image and make the real page appear in its splendor.
Have you tried using st.empty? By your description, I am assuming you have a single script providing your pages instead of using the built-in multipage functionality. The ghosting of widgets is something Streamlit does while waiting to see if it those widgets are going to be kept or discarded. If you have each page in a container within an empty element, you could have a means to tell Streamlit to just throw everything away and not wait.
import streamlit as st
import time
page_selection = st.selectbox('Page', ['A','B','C'])
page_container = st.empty()
def content(page):
page_container.empty()
time.sleep(.2) # A hack to make the empty stick.
body = page_container.container()
with body:
with st.spinner():
time.sleep(5)
st.write(page)
content(page_selection)
I must add my app is quite big, a lot more than any simple demo I found in the wild. It has something like 20 pages and around 150 plots, some of them to update in real time.
Yes, I tried with st.empty and it is even worse
No way a kitty can help me.
With st.empty, when for any reason my server is slow to return the page sometimes the frontend doesn’t wait for the whole content to be loaded and just visualize what’s available, neglecting what comes later.
That means sometimes the streamlit_option_menu is missing (it looks like extra components are injected AFTER the standard components in Streamlit), sometimes the content of the previous page is grayed out and never removed, or there are other artifacts. Adding time.sleep(0.1) after playing with st.empty doesn’t help.
I tried tons of alternative solutions, and the last one would be to hide all during the load, letting all the bad stuff happen in background, and then, tadah!, show the whole page when it is ready in its full glory.
I assume there are issues due to desynchronization between frontend and backend (like the delay associated to st.session_state)
Maybe I was naive in what Streamlit is, but I am worried that Sreamlit is more limited that I first assumed - it doesn’t seem to be able to gracefully handle even quite simple backend logical flow. Their demo gallery contains trivial examples of sites - where have all the sophisticated demos gone? Like that astro imaging one?
In any case, I seem to be butting against the tech limitations of Streamlit and am terrified this is a rewrite in something like Dash or even full JS. Neither of which I have experience of
Anyway, I’d be interested to know if you come across optimisations that have helped you
At this point, I also don’t know why I started using Streamlit at the beginning…
BUT, right now I can’t regret using it, because I love the simplicity of this framework!
At the start I struggle with a lot of things, but after some times and more knowledge of how Streamlit works I can said that almost everything could be done
I also know that I’m probably the only one in this situation, so
Alas it doesn’t work like this. st.spinner cannot be used for a container, in my case a whole page.
You see it here, in a simulation of what happens in real life:
import time
import streamlit as st
with st.spinner('Wait for it...'):
time.sleep(3)
st.title('Test app')
time.sleep(3)
st.write('1')
time.sleep(3)
st.write('2')
time.sleep(3)
st.success('Done!')
If it was linked to a container, it would show all the components in a single shot at the end of the last delay. As it is, instead, shows the various parts of the page as soon as they are ready. It is the thing I would like to avoid.
That’s why I am searching a way to “hide” the page while it is building, then show it at the end, when all has been prepared behind the curtains.
I would love to have a look at your app for inspiration. The test login sadly does not seem to work anymore.
Would it be maybe possible to reenable it?
I must be misunderstanding you, because in my tests I found the opposite. I could use st.spinner for a container, but the elements in the container were shown as they were ready, not in one single shot.
Another example of what I mean using a mock up page that is functionally very similar to the real one I am struggling with:
import streamlit as st
import time
with st.spinner("Wait..."):
st.button("Button1")
st.write("These components should appear only when the page is complete!")
time.sleep(5) # in real life here we are reading a remote DB
st.line_chart([1,2,4,3,5])
I need to calculate the whole page, and only when completed make it appear.
In the meantime there should be only the spinner.
As it works now, I see immediately [Button1] and the text, then the rest appears at the end of the elapsed time.
In this case that is easy to fix by doing the slow operation before showing the UI elements.
import streamlit as st
import time
def get_data():
time.sleep(5)
return [1, 2, 4, 3, 5]
with st.spinner("Wait..."):
data = get_data()
st.button("Button1")
st.write("These components should appear only when the page is complete!")
st.line_chart(data)
Hey,
I see it is hosted on through Akamai cdn. The app is greate and loads really fast and instantly. Could just write in short how you got it working with a cdn? and how you got it to load so fast.
I agree with @Goyo. Having a clean separation between data loading and data rendering/visualization is definitely the way to go. Then you can more easily incorporate performance optimizations in the data tier. For example, API calls can be streamed, file-based data loads can be cached or replaced with a lightweight SQLite database (e.g. :memory:), or you can incorporate functools.lru_cache and diskcache (instead of st.data_cache).
Non developers and those new to Streamlit should be aware (in my opinion) that there is often a point in your app’s development when a bit of “architecture refactoring” is required. This is especially true when you build a monolith Streamlit application with lots of functionality and doing heavy data workloads. Using st.session_state and st.data_cache can get you a long way. But, when you need more performance and responsiveness, then you must look elsewhere for solutions. I do a bit of performance profiling to find my app’s hot spots and then off load them through some simple refactoring and design changes. I find this almost always boils down to optimizing how I sling data around in my app including but not limited to how data is formatted.
P.S. Everyone needs to keep this diagram in mind when building data apps (courtesy of bytebytego.com)
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.