How to fix lagging after Edit and Delete button clicks in Streamlit
23:27 08 Jan 2026

I have a small Streamlit app for creating materials.
It just has a dropdown menu and stores created materials until you click "Download".

It was working fine til I added a "Delete" button.

Now I have to click on "Edit" twice for it to populate the dropdown menus. And when I click "Delete" it says the material has been deleted, but it's still showing on the screen until I select something from the dropdown menus.

Any help much appreciated.

here's a small sample excel

screenshot

from datetime import datetime as dt
import streamlit as st
import pandas as pd
from io import BytesIO
import uuid

st.set_page_config(layout="wide")
st.title("Material Creation")

# -------------------------------------------------
# Session state initialization
# -------------------------------------------------
if "materials_df" not in st.session_state:
    st.session_state.materials_df = pd.DataFrame()

if "edit_id" not in st.session_state:
    st.session_state.edit_id = None

if "custom_inputs" not in st.session_state:
    st.session_state.custom_inputs = {}

# -------------------------------------------------
# Load source Excel
# -------------------------------------------------
@st.cache_data
def load_data(path="Sample.xlsx"):
    return pd.read_excel(path)

df = load_data("Sample.xlsx")

# -------------------------------------------------
# Menu headers
# -------------------------------------------------
menu_config_headers = {
    'Polymer': ['Manufacturer', 'Subtype', 'Product Name', 'Grade', 'Color'],
    'Coating': ['Manufacturer', 'Subtype', 'Product Name', 'Grade', 'Color'],
    'Misc': ['Manufacturer', 'Subtype', 'Product Name', 'Grade', 'Color'],
    'Solder Paste': ['Manufacturer', 'Grade', 'Flux', 'Metal Load', 'Powder Size', 'Recycled Content', 'Solder Alloy'],
    'Solder Alloy': ['Manufacturer', 'Grade', 'Form', 'Recycled Content', 'Perform Size', 'Solder Alloy'],
    'Shaped Metal': ['Manufacturer', 'Subtype', 'Product Name', 'Grade', 'Form', 'Metal/Alloy', 'Recycled Content', 'Standard and Standard Designating Body', 'Thickness'],
    'Bulk Metal': ['Manufacturer', 'Grade', 'Form', 'Metal/Alloy', 'Purity/Concentration', 'Recycled Content', 'Standard and Standard Designating Body'],
    'Plating': ['Manufacturer', 'Form', 'Manufacturer Performing Plating', 'Metal/Alloy', 'Purity/Concentration', 'Recycled Content'],
    'Chemical': ['Manufacturer', 'Chemical Name / Description', 'Purity/Concentration'],
    'Magnet': ['Manufacturer', 'Subtype', 'Grade', 'Recycled Content'],
    'Process': ['Manufacturer', 'Subtype', 'Product Name', 'Grade'],
    'Semiconductor': ['Manufacturer', 'Grade', 'Semiconductor Description', 'Semiconductor Type'],
    'Surface Treatment Process': ['Manufacturer', 'Subtype', 'Color', 'Composition of Coating / "Target" Material', 'Purity/Concentration', 'Substrate: Base Metal', 'Surface Treatment Type / Specification'],
    'Anodization': ['Manufacturer', 'Color', 'Anodization Details', 'Metal + "Anodizing"', 'Substrate: Base Metal']
}

@st.cache_data
def build_menu_config(df, headers):
    menu = {}
    for mat, cols in headers.items():
        sub = df[df["Material"] == mat]
        menu[mat] = {
            c: sub[c].dropna().unique().tolist() if c in sub.columns else []
            for c in cols
        }
    return menu

menu_config = build_menu_config(df, menu_config_headers)

# -------------------------------------------------
# Layout
# -------------------------------------------------
form_col, preview_col = st.columns([1.2, 1])

# =================================================
# FORM
# =================================================
with form_col:
    # Get existing row if editing
    existing_row = None
    if st.session_state.edit_id:
        existing_row = st.session_state.materials_df[
            st.session_state.materials_df["ID"] == st.session_state.edit_id
        ].squeeze()  # Series

    category = st.selectbox(
        "Type",
        [""] + list(menu_config.keys()),
        index=0 if existing_row is None else list(menu_config.keys()).index(existing_row["Type"]) + 1
    )

    selected_values = {}

    if category:
        for label, options in menu_config[category].items():
            options_with_add = ["", "➕ Add new"] + options

            existing_value = existing_row[label] if existing_row is not None and label in existing_row else None

            choice = st.selectbox(
                label,
                options_with_add,
                index=options_with_add.index(existing_value) if existing_value in options_with_add else 0,
                key=f"select-{label}"
            )

            if choice == "➕ Add new":
                custom_value = st.text_input(
                    f"Enter new {label}",
                    value=existing_value or "",
                    key=f"input-{label}"
                )
                selected_values[label] = custom_value
            else:
                selected_values[label] = choice or None

        record = {"Type": category, **selected_values}

        # -------------------------------------------------
        # Buttons
        # -------------------------------------------------
        col1, col2 = st.columns(2)

        with col1:
            if st.button("Create Material"):
                if not st.session_state.edit_id:
                    # Add new material
                    record["ID"] = str(uuid.uuid4())
                    st.session_state.materials_df = pd.concat(
                        [st.session_state.materials_df, pd.DataFrame([record])],
                        ignore_index=True
                    )
                else:
                    # Update existing material by ID
                    row_idx = st.session_state.materials_df.index[
                        st.session_state.materials_df["ID"] == st.session_state.edit_id
                    ][0]
                    for k, v in record.items():
                        st.session_state.materials_df.loc[row_idx, k] = v
                    st.session_state.edit_id = None

                st.success("Material saved")

        with col2:
            if not st.session_state.materials_df.empty:
                output = BytesIO()
                with pd.ExcelWriter(output, engine="xlsxwriter") as writer:
                    st.session_state.materials_df.drop(columns=["ID"], errors="ignore").to_excel(
                        writer, index=False, sheet_name="Materials"
                    )

                timestamp = dt.now().strftime("%Y%m%d")
                st.download_button(
                    "Download materials",
                    data=output.getvalue(),
                    file_name=f"Materials_{timestamp}.xlsx",
                    mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
                )

# =================================================
# PREVIEW / EDIT / DELETE
# =================================================
with preview_col:
    st.subheader("Created Materials")

    if st.session_state.materials_df.empty:
        st.info("No materials created yet.")
    else:
        delete_uid = None
        edit_uid = None

        for _, row in st.session_state.materials_df.iterrows():
            uid = row["ID"]

            with st.container():
                for field, value in row.dropna().items():
                    if field != "ID":
                        st.markdown(f"**{field}:** {value}")

                # Buttons
                btn_col1, btn_col2 = st.columns([1, 1])
                with btn_col1:
                    if st.button("Edit", key=f"edit-{uid}"):
                        edit_uid = uid
                with btn_col2:
                    if st.button("Delete", key=f"delete-{uid}"):
                        delete_uid = uid

        # Apply delete/edit after loop
        if delete_uid:
            st.session_state.materials_df = st.session_state.materials_df[
                st.session_state.materials_df["ID"] != delete_uid
            ]
            if st.session_state.edit_id == delete_uid:
                st.session_state.edit_id = None
            st.success("Material deleted")
            st.stop()

        if edit_uid:
            st.session_state.edit_id = edit_uid
            st.stop()
debugging streamlit