Widgets appear two times during "running"


In my code, I use some different widgets like the st.upload, st.data_editor and st.button. The CRUD app does what it shall do … but I have a strange behaviour.
During upload (process takes some seconds) as well as after clicking the button (after that also a process starts) any widget (e.g. the data_editor, the button, OR the uploader) appears then two times - one of them enabled, the other one greyed out - as long as the “running” process is finished.
When the process is finished and the app in idle, everything is fine and no double widgets.
Whats could be the problem here?
I guess it has something to do with the session_state variables.

Debug info

  • Streamlit version: 1.27.2
  • Python version: 3.9.17
  • Using Conda? PipEnv? PyEnv? Pex?
  • OS version:
  • Browser version:

Requirements file

Using Conda? PipEnv? PyEnv? Pex? Share the contents of your requirements file here.
Not sure what a requirements file is? Check out this doc and add a requirements file to your app.


  • Link to your GitHub repo:
  • Link to your deployed app:

Additional information

If needed, add any other context about the problem here.

Could you please share a small reproducible code snippet that shows the behavior you’re seeing?

Hi @blackary,

you’ll see, it´s very very strange.
Don’t think about the sense of the code itself, it’s heavily reduced, but shows the behaviour.

If you use the code like this, with a mysql database in the background, each second click on the button the data_editor appears a second time under the first one in grey until the query is done (2-3 seconds)

Behaviour 1:

If I eliminate this line:

return conn.query('SELECT * from DB;')

and take therefore this one:

return pd.DataFrame(np.random.randint(0,100,size=(10000, 3)), columns=list('ABC')) 

no second data editor appears. But, what has the return query to do with the display/rendering?

Behaviour 2:

If you eliminate the st.columns line, and set all col1 back to st, following appears:
The data_editor does not appear a second time, therefore instead the st.button.

Full code to test see here - but you need an sql database for testing.

import streamlit as st
import time

def init_connection():
    return st.experimental_connection('mysql',type='sql')

conn = init_connection()

def run_query():
    return conn.query('SELECT * from DB;')
    #return pd.DataFrame(np.random.randint(0,100,size=(10000, 3)), columns=list('ABC')) 

if "df" not in st.session_state:
    st.session_state['df'] = run_query()
col1, col2 = st.columns([8,2])

if "df_ed" not in st.session_state:
    st.session_state["df_ed"] = st.session_state["df"]

# Create data_editor @ column 1
st.session_state["df_ed"]=col1.data_editor(st.session_state['df'],hide_index=True, use_container_width=True)

if col1.button('Save dataframe'):
    with conn.session as s:
        time.sleep(5) #Save the dataframe
        del st.session_state["df"]
        del st.session_state["df_ed"]

Thanks – super helpful! Turns out it’s related to the cache that conn.query does under the hood.

I tested with streamlit-nightly, because there is now an argument to hide the spinner on the built-in caching. After adding this kwarg, the problem is resolved.

If you don’t want to use streamlit-nightly, the next release should be coming out soon, and you can wait on that.

import streamlit as st
import time

conn = st.connection("mysql", type="sql") # Note: this is automatically cached with st.cache_resource, so no need to cache it yourself

if "df" not in st.session_state:
    st.session_state["df"] = conn.query(
        "SELECT * from table", ttl=600, show_spinner=False # Note that this is also cached automatically, and this has a new show_spinner=False that will come out with streamlit 1.28

if st.button("Save dataframe"):