Forms stop expensive reruns while users fill several fields, and match how people expect to enter data.
Normally each widget change triggers a rerun. Inside `st.form`, widget changes are held in the browser until the user clicks the form's submit button; then the script reruns once with all the new values.
with st.form("signup"):
email = st.text_input("Email")
plan = st.radio("Plan", ["free", "pro"])
submitted = st.form_submit_button("Sign up")
if submitted:
save(email, plan)Rules:
- Every form needs at least one
st.form_submit_button; Streamlit shows an error otherwise. - Ordinary
st.buttonandst.download_buttonare not allowed inside forms. - Only the submit button can have a callback inside a form; widgets inside cannot use
on_change. Useon_clickon the submit button, which runs before the rerun with all form values available in session state (via keys). clear_on_submit=Trueresets the inputs after a submission, useful for entry forms.enter_to_submitcontrols whether pressing Enter in a text field submits.
Before submission, widget return values are their previous (or default) values, so read values in the if submitted: branch.
Forms are ideal when inputs only make sense together (a search with several filters, a record to save), or when each rerun is expensive. For instant feedback on every change, use plain widgets.
Example
import streamlit as st
if "orders" not in st.session_state:
st.session_state.orders = []
def place_order(): # submit callback: values are in state
s = st.session_state
s.orders.append({"item": s.item, "qty": s.qty, "rush": s.rush})
with st.form("order", clear_on_submit=True):
st.selectbox("Item", ["Tea", "Coffee", "Cocoa"], key="item")
st.number_input("Quantity", min_value=1, max_value=20, value=1, key="qty")
st.checkbox("Rush delivery", key="rush")
submitted = st.form_submit_button("Place order", on_click=place_order)
if submitted:
st.success(f"Order #{len(st.session_state.orders)} placed")
st.write(st.session_state.orders)
# Changing the inputs does not rerun the app; only clicking the button does.Remember
- Widgets inside
st.formonly trigger a rerun when the form is submitted - Each form needs a
st.form_submit_button; only it may have a callback - Read values after submission, or in the submit callback via keys
Watch out for
- Adding
on_changecallbacks to widgets inside a form, which Streamlit does not allow.