Reducing Latency in Streamlit with st.fragment
Stop the full-page flicker. Learn how to use st.fragment to isolate expensive computations and create high-performance, responsive Streamlit dashboards.
17 Feb 2026, 21:41 UTC

The Cost of the Full Rerun
Streamlit's execution model is straightforward: whenever a user interacts with a widget, the entire script runs from top to bottom. While this simplifies development, it creates a performance bottleneck when your app mixes lightweight UI controls with heavy computations. If you have a data-heavy dashboard where a simple slider update triggers a five-second ML inference or a massive SQL query, the user experience suffers from noticeable lag.
The st.fragment decorator (introduced in version 1.33) solves this by allowing you to isolate specific sections of your UI. When a widget inside a fragment changes, only the code within that decorated function re-executes, leaving the rest of the page frozen.
How Fragments Isolate Execution
A fragment is essentially a scoped rerun. By wrapping a block of code in @st.fragment, you tell Streamlit to treat that function as an independent unit of execution. This is particularly useful for "interactive zones"—like a chart with its own set of filters—that don't need to trigger a refresh of the entire page header, sidebar, or other unrelated data tables.
Key behaviors of fragments include:
- Automatic Triggering: Any widget interaction inside the fragment automatically triggers a rerun of only that fragment.
- State Isolation: While fragments can access
st.session_state, they operate as a distinct execution block. - Execution Scope: Fragments cannot contain page-level configurations like
st.set_page_configorst.navigation.
Practical Implementation: The Interactive Filter
Consider a scenario where you have a global app state (like a user profile) and a specific, expensive data visualization. Without fragments, changing a filter on the visualization would force the app to re-verify the user profile and re-render the entire layout.
import streamlit as st
import time
import pandas as pd
import numpy as np
st.title("Performance Dashboard")
# This part is expensive and runs on every full rerun
st.write("Loading global app state...")
time.sleep(2)
st.success("Global state loaded!")
@st.fragment
def render_data_explorer():
st.subheader("Data Explorer")
# This widget only triggers the fragment, not the whole script
threshold = st.slider("Select Threshold", 0, 100, 50)
st.write(f"Filtering data for threshold: {threshold}")
# Simulate a medium-weight operation
time.sleep(0.5)
df = pd.DataFrame(np.random.randn(10, 2), columns=['A', 'B'])
st.line_chart(df)
render_data_explorer()
Verification: To verify this is working, open your browser's Developer Tools (F12) and go to the Network tab. When you move the slider, you will see a POST request to /_stcore/fragment rather than a full stream request. The "Global state loaded!" message will not flicker or reappear, confirming the rest of the page remained static.
Constraints and Engineering Trade-offs
While fragments significantly reduce latency, they introduce specific constraints that can lead to bugs if ignored:
Widget Key Collisions
Every widget in a Streamlit app must have a unique key. Even if two widgets are in different fragments, they share the same global namespace. If you use a generic key like key="filter_input" in two different fragments, Streamlit will throw a DuplicateWidgetID error.
The Rerun Scope
Using a standard st.rerun() inside a fragment will trigger a full-page refresh, effectively bypassing the fragment's benefit. To force a refresh of only the fragment, you must use st.rerun(scope="fragment") (available in version 1.37+).
Nesting Limitations
Fragments cannot be nested. You cannot define a fragment inside another fragment. If your UI requires multiple independent update zones, define them as separate, top-level decorated functions.
Decision Matrix: Fragment vs. Full Rerun
| Scenario | Recommended Approach | Reasoning |
|---|---|---|
| Global setting change (e.g., Theme/User) | Full Rerun | Affects the entire app state and layout. |
| Local chart filter/slider | st.fragment |
Prevents expensive global re-computations. |
| Multi-step form submission | st.session_state + Full Rerun |
Ensures data consistency across different page sections. |
Summary Checklist
To implement fragments successfully, ensure the following:
- Verify your Streamlit version is
>= 1.33.0usingst.__version__. - Assign unique keys to all widgets within the fragment to avoid ID collisions.
- Move any logic that must update the rest of the page outside of the fragment.
- Use
st.rerun(scope="fragment")for internal updates.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.