I love Streamlit for its speed of development. Being able to turn a Python script into a web app in minutes is a superpower. However, as my apps grew from simple prototypes to tools handling millions of rows of data, I hit a wall: the ‘Rerun’ problem. If you’ve felt your app stutter every time a user moves a slider, you’re dealing with the core challenge of streamlining streamlit performance optimization.

The fundamental issue is that Streamlit reruns the entire script from top to bottom whenever a user interacts with a widget. While this simplifies state management, it’s a nightmare for performance if you’re reloading a 500MB CSV or querying a slow API on every single interaction. In this deep dive, I’ll show you how to break this cycle and build apps that feel instantaneous.

The Challenge: The Execution Model Bottleneck

To optimize, we first have to understand the bottleneck. By default, Streamlit is stateless. Every interaction triggers a full script execution. When I first started building these tools, I didn’t realize that my pd.read_csv() call was happening every time a user changed a dropdown menu. This leads to high latency, wasted CPU cycles, and a frustrating user experience.

If you’re still deciding between frameworks, you might want to check out my comparison of Streamlit vs Dash for data apps to see which execution model fits your needs better. But if you’re committed to Streamlit, the solution lies in selective execution.

Solution Overview: The Optimization Pyramid

I categorize performance wins into three layers: Caching (avoiding redundant work), Session State (preserving data), and Fragments (reducing the rerun scope). By applying these in order, you move from an app that feels like a script to one that feels like a professional software product.

Techniques for High-Performance Streamlit Apps

1. Mastering @st.cache_data and @st.cache_resource

Caching is the single most impactful way of streamlining streamlit performance optimization. Streamlit provides two distinct decorators: @st.cache_data for data (DataFrames, lists) and @st.cache_resource for global objects (Database connections, ML models).

import streamlit as st
import pandas as pd
import time

@st.cache_data
def load_massive_dataset(file_path):
    # This only runs once unless the file_path changes
    time.sleep(3) # Simulating a slow network load
    return pd.read_csv(file_path)

df = load_massive_dataset("large_data.csv")
st.write(df)

In my experience, the biggest mistake developers make is caching functions that depend on frequently changing variables. Keep your cached functions pure—they should only depend on the arguments you pass to them.

2. Reducing Rerun Scope with @st.fragment

Introduced in recent versions, fragments allow you to rerun only a specific function instead of the whole page. This is a game-changer for apps with a “global” header and a “local” interactive chart.

@st.fragment
def interactive_chart():
    val = st.slider("Adjust threshold", 0, 100, 50)
    # Only this function reruns when the slider moves!
    st.line_chart(data[data['value'] > val])

st.title("My Heavy Dashboard")
# This part doesn't rerun when the slider in the fragment moves
st.write("Global Header Content") 
interactive_chart()

3. Optimizing Data Visualization

The way you render data is just as important as how you load it. Using heavy libraries like Plotly for simple charts can slow down the browser. I often recommend looking at the best python data visualization library 2026 list to find a balance between interactivity and render speed.

As shown in the benchmark chart below, switching from full-page reruns to fragmented updates can reduce perceived latency by up to 80%.

Benchmark chart showing latency reduction using Streamlit fragments vs full page reruns
Benchmark chart showing latency reduction using Streamlit fragments vs full page reruns

Implementation Strategy: A Case Study

I recently optimized a financial reporting tool that took 12 seconds to update. Here was my workflow:

Pitfalls to Avoid

Ready to scale your data tools? If you’re building for an enterprise environment, make sure you’re following a consistent architecture to avoid technical debt.