Using a dialog to confirm an overwrite

I have two fields on a StreamLit page hosted in a Databricks App.
The user fills in values for these fields.
The first value is a unique key in a underlying table.
With values in these fields, the user can click a “Save” button, or a “New” button.

This question is about the actions behind the “Save” button.
On “Save” button click a query is triggered to see if the key value entered already exist in a table.
If if does already exist then a confirmation dialog is presented with a “Cancel” button and a “Overwrite” button.
The intent is that “Cancel” returns the user back to the original page nothing else done.
But if the user click “Overwrite” then the values are written to the table (updating the value of the second field of the table given the associated key).

However. The confirmation dialog presents on screen then immediately disappears without the user having an opportunity to click their “Cancel” / “Overwrite” option because, in code, you have to get a “st.rerun()” to resolve the dialog. But st.rerun() also means the confirmation dialog is not going to wait for the user.

Thanking you in advance.
The following is a mock up:

import streamlit as st


def overwrite_warning():
    st.warning("overwrite warning")
    btn_cncl, btn_ovwt, blkspc = st.columns([0.2, 0.3, 0.5])
    with btn_cncl:
        cncl = st.button("Cancel")
    with btn_ovwt:
        ovwt = st.button("Overwrite")
    if cncl:
        return('CancelDontSave')
    if ovwt:
        return('OkayToOverwrite')
     return(None)


@st.dialog('zz_dialogtest')
def my_dialog():
    r = overwrite_warning()
    return(r)

def btn_save_process(process_name, process_desc):
        e = True if process_name == 'steve' else False
        if e:
            r = str(my_dialog()) # "None" / "CancelDontSave" / "OkayToOverwrite"
            if r == 'OkayToOverwrite':
                st.write('upsert to the table')
    

pn = st.text_input("Process Name", value= "steve")
pd = st.text_input("Process desc", value= "")

if st.button("Save"):
    btn_save_process(pn, pd)
    st.rerun()

Looking at your code, I can identify exactly why your confirmation dialog appears and immediately disappears:

if st.button("Save"):
    btn_save_process(pn, pd)
    st.rerun()  # <-- This is the problem

What’s happening:

  1. User clicks “Save” button
  2. btn_save_process() runs and calls my_dialog() to display your confirmation dialog
  3. But before the user can interact, the very next line st.rerun() executes
  4. This forces Streamlit to reload the entire app, causing the dialog to disappear

Simply remove the st.rerun() line.

In Streamlit, button clicks already trigger a rerun automatically, so your explicit st.rerun() is causing a second unwanted rerun that’s interrupting the dialog.

Without that line, the workflow will be:

  1. User clicks “Save” → dialog appears
  2. User clicks “Cancel” or “Overwrite” → those buttons trigger their own natural rerun
  3. During that rerun, your code can process the dialog result as expected

No additional state management is needed for this specific issue - just remove the problematic rerun.

Tinkering with my mock up this morning I found you are correct.
But there is something in your remark to re-consider. I do not find “clicking a dialog the button automatically call rerun()”.
By remarking out the rerun() you are pointing to now the dialog now never goes away no matter how much button clicking you ezecute.

So if I remove the returns() from overwrite_warning() and add rerun() instead to each of if cncl & if ovwt then I get the expected/desired behavior.

This also implies I will now have to do everything else necessary for cancel opr overwrite before hitting either of these two new st.rerun()s.

Thanks for you input. Highly valued.

After understanding your problem better, I found a solution to your problem:

import streamlit as st

# Initialize session state for storing dialog result
if 'dialog_result' not in st.session_state:
    st.session_state.dialog_result = None

@st.dialog('zz_dialogtest')
def my_dialog():
    st.warning("overwrite warning")
    btn_cncl, btn_ovwt, blkspc = st.columns([0.2, 0.3, 0.5])
    with btn_cncl:
        cncl = st.button("Cancel")
    with btn_ovwt:
        ovwt = st.button("Overwrite")
    if cncl:
        st.session_state.dialog_result = 'CancelDontSave'
        st.rerun()
    if ovwt:
        st.session_state.dialog_result = 'OkayToOverwrite'
        st.rerun()

def btn_save_process(process_name, process_desc):
        e = True if process_name == 'steve' else False
        if e:
            my_dialog() # "None" / "CancelDontSave" / "OkayToOverwrite"

if "dialog_result" in st.session_state and st.session_state.dialog_result is not None:
    if st.session_state.dialog_result == 'OkayToOverwrite':
        st.write('upsert to the table')
    st.session_state.dialog_result = None

pn = st.text_input("Process Name", value= "steve")
pd = st.text_input("Process desc", value= "")

if st.button("Save"):
    btn_save_process(pn, pd)

Why This Works

This solution works because of how Streamlit’s execution model handles state and reruns:

  1. First run:
  • User clicks “Save”
  • Dialog appears
  • Script execution finishes
  1. Second run (after user clicks “Overwrite” in dialog):
  • Button click sets st.session_state.dialog_result = ‘OkayToOverwrite’
  • st.rerun() triggers a new script execution
  • The if “dialog_result” in st.session_state… part runs and sees the stored value
  • “upsert to the table” message is displayed
  • Dialog result is reset for next use

The key insight is using session_state as a “memory” that persists between reruns, allowing the script to “remember” what happened in the dialog even though the script restarts.

Importantly, you’re not trying to capture the dialog’s return value directly (which would be impossible due to the rerun), but instead checking the stored session state value on the next run.

I like how your solution takes button click actions out of def my_dialog().
I have a feature in my end solution where I need to take actions in the script where you’re testing "dialog_result" in session_state.
Seeing what you’re presenting seems like it will go a long way to that end goal.
Thanks.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.