Sunday, May 31, 2026

Published May 31, 2026 by with 0 comment

How Can I Re-render Only What Changed in Streamlit So It Runs Faster?

If you've written much Streamlit code, you've likely run into a change to one piece of the state (very slowly) re-rendering every plot and table in your app. Is there any way to avoid this?Streamlit Fragments are an answer to this. Demonstrating how to use them makes it really clear, so consider this example:

import numpy as np
import streamlit as st

st.set_page_config(layout="wide")
def plot_cell(plot_number):
    """Render a single plot with its own amplitude slider (not in sidebar)."""
    amplitude = st.slider(
        f"Plot {plot_number} amplitude",
        min_value=1,
        max_value=100,
        value=10,
        key=f"amp_{plot_number}",
    )
    random_values = np.random.default_rng().uniform(-amplitude, amplitude, size=4000)
    st.line_chart(random_values, height=150)


plot_number = 1
for _row in range(2):
    cols = st.columns(5)
    for col_idx in range(5):
        with cols[col_idx]:
            plot_cell(plot_number)
        plot_number += 1

That will generate 2 rows of 5 plots per row and a slider per plot. Each plot will be 4000 random values between +/- slider value.

Now, imagine what will happen if you change the slider for the second plot? It will update the amplitude of that plot, but what about all the others that are unchanged? Watch the gif below:
By default, streamlit will re-run the entire app and re-render everything. It has to remake and draw all 10 plots which can be quite slow.

Now consider this nearly identical code; the only change is the addition of the @st.fragment above the slider + plot code:

import numpy as np
import streamlit as st

st.set_page_config(layout="wide")


@st.fragment
def plot_cell(plot_number):
    """Render a single plot with its own amplitude slider (not in sidebar)."""
    amplitude = st.slider(
        f"Plot {plot_number} amplitude",
        min_value=1,
        max_value=100,
        value=10,
        key=f"amp_{plot_number}",
    )
    random_values = np.random.default_rng().uniform(-amplitude, amplitude, size=4000)
    st.line_chart(random_values, height=150)


plot_number = 1
for _row in range(2):
    cols = st.columns(5)
    for col_idx in range(5):
        with cols[col_idx]:
            plot_cell(plot_number)
        plot_number += 1

How is that different? Everything inside the fragment will only rerun the fragment on change. If you change the second plot's slider, only the second plot will be regenerated. What does that look like?
Massive performance improvement. This only works for self-contained blocks of code like the slider + plot combination here, but those are extremely common in Streamlit apps so this can be really useful when working with Streamlit.
      edit

0 comments:

Post a Comment