Greyed out widgets

Streamlit Version 1.50.0
Python 3

The app in question is running on an SBC, but I am networked with it and running the app my Mac.

This is a more general “flow” question…

I am new to Streamlit, and the app I am writing keeps running into greyed out elements being displayed.

I have taken time to read the docs (fundamentals section, and concepts section ), plus I have been searching the forum to try and figure out what I am doing wrong.

I am using “stages” to try and show different things on the screen at different times.

In stage 0, I’m displaying only button.
In stage 1, I’m displaying an image, a form (with a form button) plus another separate button outside the form.

I am using session_state to manage the “stage”, and each button updates session state.

The program goes like this:

if st.session_state.stage == 0:
with st.container():
st.button(“Take Picture”, on_click=create_data, key=“take_pic”):

if st.session_state.stage == 1:
    with st.container():
         st.image(st.session_state.current_image) # Display Picture
         with st.form('item_form'):
              …form fields

              st.form_submit_button("Save to Database", on_click=save_to_db)

         st.button("Reset Item", on_click=reset, key="reset_item")

The on_click functions do certain tasks, like taking a picture and making a single LLM call to analyze the picture PLUS they all change the session_state.stage

My issue is that I keep getting greyed out widgets sticking around.

At first it was the form, but using a container fixed that. Now it is the buttons. They just will not go away - after pressed, they stick around greyed out. My first button is weird - the first time I press it, it does not grey out - works like a charm. The second time it is pressed, it sticks around greyed out below the other widgets.

Maybe I am missing something on how to correctly develop an app with Streamlit? I understand that the entire app gets rerun every time a widget is interacted with, and when I plan out the stages, I “feel” like I am thinking through it correctly, but any general thoughts or tips would be much appreciated.

Here is the main file in the repo.

Welcome to the Streamlit community! :balloon: Thanks for sharing your question and describing your approach so clearly—you’re definitely not alone in running into “stale” or greyed-out widgets when managing multi-stage flows with session state.

The greyed-out (stale) widgets appear because Streamlit replaces elements one-by-one during a rerun, and only removes leftover elements at the end of the script. If your script’s logic doesn’t fully clear or replace previous widgets, those widgets can linger in a disabled state. Using containers (like st.empty or st.container) is the recommended way to control which elements are visible at each stage. For buttons, wrapping them in an st.empty() and calling .empty() before rendering the next stage can help ensure they’re removed immediately. Also, make sure your stage logic is mutually exclusive and that you don’t accidentally render widgets from multiple stages at once. See this forum thread and Streamlit’s button/container docs for practical examples.

Here’s a minimal pattern to manage stage-based flows and avoid stale widgets:

import streamlit as st

if 'stage' not in st.session_state:
    st.session_state.stage = 0

def next_stage():
    st.session_state.stage += 1

if st.session_state.stage == 0:
    btn_placeholder = st.empty()
    if btn_placeholder.button("Take Picture", on_click=next_stage, key="take_pic"):
        btn_placeholder.empty()
elif st.session_state.stage == 1:
    st.image("your_image.png")
    with st.form('item_form'):
        # form fields here
        st.form_submit_button("Save to Database", on_click=next_stage)
    st.button("Reset Item", on_click=lambda: st.session_state.update(stage=0), key="reset_item")

If you can share a minimum reproducible example or your repo, folks here can help debug further! And if you haven’t already, check out the FAQ on stale elements for more tips. Community members, feel free to jump in with your own tricks!

Sources:

The FAQ added a little clarity thanks to @mathcatsand

I had been using this code hack from @Raghu2 at the end of the app to clear the stale/greyed/ghosted elements from the screen:

for i in range(0, 2):

   st.markdown(" ")

But I did not understand why the hack worked - I thought it had to do with adding addition st.xyz commands at at the end.

But now that I see it is a timing issue (bug?) for allowing the screen to clear and I have added time.sleep(.1) to the end of the app and everything is working like I had anticipated, and this hack is a little easier to sleep at night with.

Many thanks forum contributors!!