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)