From 55cc197be514af394662242175c90a0d0c1abcfa Mon Sep 17 00:00:00 2001
From: Patrick Plettner <17936703+pplettner@users.noreply.github.com>
Date: Wed, 17 Jun 2026 18:29:28 +0200
Subject: [PATCH 1/7] Add parallel fragments description
---
.../api-reference/control-flow/fragment.md | 2 +-
.../concepts/architecture/fragments.md | 61 +++++++++++++++++++
2 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/content/develop/api-reference/control-flow/fragment.md b/content/develop/api-reference/control-flow/fragment.md
index b37fbd885..f3c3ac770 100644
--- a/content/develop/api-reference/control-flow/fragment.md
+++ b/content/develop/api-reference/control-flow/fragment.md
@@ -2,7 +2,7 @@
title: st.fragment
slug: /develop/api-reference/execution-flow/st.fragment
description: st.fragment is a decorator that allows a function to rerun independently from the rest of the script.
-keywords: st.fragment, fragment, decorator, rerun, independent, execution flow, control flow, run_every, experimental_fragment
+keywords: st.fragment, fragment, decorator, rerun, independent, execution flow, control flow, run_every, experimental_fragment, parallel, parallel fragments, concurrency
---
diff --git a/content/develop/concepts/architecture/fragments.md b/content/develop/concepts/architecture/fragments.md
index 89ba288fc..680c47511 100644
--- a/content/develop/concepts/architecture/fragments.md
+++ b/content/develop/concepts/architecture/fragments.md
@@ -18,6 +18,7 @@ Fragments are versatile and applicable to a wide variety of circumstances. Here
- Your app has multiple visualizations and each one takes time to load, but you have a filter input that only updates one of them.
- You have a dynamic form that doesn't need to update the rest of your app (until the form is complete).
- You want to automatically update a single component or group of components to stream data.
+- Your app has several slow, independent operations (like database queries or API calls) that you want to run at the same time instead of one after another.
## Defining and calling a fragment
@@ -77,6 +78,8 @@ If you run the code above, the full script will run top to bottom on your app's

+By default, fragments run inline on the main thread, in the order you call them, just like the rest of your script. If you have slow, independent fragments, you can opt in to running them concurrently during full-app reruns with `parallel=True`. See [Run fragments in parallel](#run-fragments-in-parallel) below.
+
## Fragment return values and interacting with the rest of your app
Streamlit ignores fragment return values during fragment reruns, so defining return values for your fragment functions is not recommended. Instead, if your fragment needs to share data with the rest of your app, use Session State. Fragments are just functions in your script, so they can access Session State, imported modules, and other Streamlit elements like containers. If your fragment writes to any container created outside of itself, note the following difference in behavior:
@@ -89,6 +92,53 @@ To prevent elements from accumulating in outside containers, use [`st.empty`](/d
If you need to trigger a full-script rerun from inside a fragment, call [`st.rerun`](/develop/api-reference/execution-flow/st.rerun). For a related tutorial, see [Trigger a full-script rerun from inside a fragment](/develop/tutorials/execution-flow/trigger-a-full-script-rerun-from-a-fragment).
+## Run fragments in parallel
+
+By default, fragments run inline on the main thread, in the order you call them. For fragments that can be run independent of each other (such as database queries or external API calls), you can run these concurrently by setting `parallel=True` in the `st.fragment` decorator.
+
+```python
+import streamlit as st
+
+@st.fragment(parallel=True)
+def slow_chart():
+ data = expensive_query()
+ st.line_chart(data)
+
+@st.fragment(parallel=True)
+def slow_table():
+ data = another_expensive_query()
+ st.dataframe(data)
+
+slow_chart()
+slow_table()
+```
+
+When `parallel=True`, the behavior depends on the type of rerun:
+
+- **During a full-app rerun**, Streamlit dispatches the fragment to a thread pool. It runs concurrently with your other parallel fragments and with the rest of your main script, rather than blocking on each fragment in turn. If `slow_chart` and `slow_table` each take two seconds, running them in parallel lets your app finish in about two seconds instead of four.
+
+- **During a fragment rerun** (when a user interacts with a widget inside the fragment), execution stays sequential. The fragment runs by itself on the main thread, exactly like a non-parallel fragment. This keeps your state updates predictable when a user is actively interacting with a fragment.
+
+Parallel fragments are most helpful when each fragment does independent, time-consuming work. If your fragments are fast or depend on each other's results, running them in parallel adds complexity without a meaningful speedup.
+
+### Restricted commands during parallel execution
+
+Because parallel fragments run concurrently on separate threads during the initial (full-app) run, a few Streamlit commands aren't safe to call from inside them and will raise an error. These include:
+
+- [`st.dialog`](/develop/api-reference/execution-flow/st.dialog)
+- [`st.switch_page`](/develop/api-reference/navigation/st.switch_page)
+- Writing to containers created outside the fragment.
+
+These commands work normally during a fragment rerun (for example, after a user interacts with a widget inside the fragment), because fragment reruns are sequential. If you need one of these commands, call it from a non-parallel fragment or from the main body of your script.
+
+### Thread safety and shared state
+
+Parallel fragments can run at the same time, so they can read and write shared resources concurrently. This includes Session State, global variables, files, and external connections. To avoid race conditions:
+
+- Prefer having each parallel fragment write to its **own** Session State keys, then read the combined results back in your main script after the fragments finish.
+- Avoid having two parallel fragments mutate the same Session State key, list, or dictionary at the same time.
+- If parallel fragments must share a mutable resource, coordinate access explicitly (for example, with a `threading.Lock`).
+
## Automate fragment reruns
`st.fragment` includes a convenient `run_every` parameter that causes the fragment to rerun automatically at the specified time interval. These reruns are in addition to any reruns (fragment or full-script) triggered by your user. The automatic fragment reruns will continue even if your user is not interacting with your app. This is a great way to show a live data stream or status on a running background job, efficiently updating your rendered data and _only_ your rendered data.
@@ -103,6 +153,17 @@ def auto_function():
auto_function()
```
+You can combine `run_every` with `parallel=True`. The automatic reruns triggered by `run_every` are fragment reruns, so they execute sequentially (just like reruns triggered by a user). The `parallel=True` setting only changes how the fragment behaves during a full-app rerun. This combination is handy when you have several independent, auto-updating data feeds that you want to load concurrently on each full-app rerun:
+
+```python
+@st.fragment(parallel=True, run_every="5s")
+def live_metrics():
+ data = fetch_latest_metrics()
+ st.metric("Active Users", data["users"])
+
+live_metrics()
+```
+
For a related tutorial, see [Start and stop a streaming fragment](/develop/tutorials/execution-flow/start-and-stop-fragment-auto-reruns).
## Compare fragments to other Streamlit features
From 9ef3044f5da394d0ca2cc48b5a05b469e1d4056c Mon Sep 17 00:00:00 2001
From: Patrick Plettner <17936703+pplettner@users.noreply.github.com>
Date: Fri, 19 Jun 2026 18:22:34 +0200
Subject: [PATCH 2/7] Address suggestions in PR 1490
---
.../concepts/architecture/fragments.md | 20 ++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/content/develop/concepts/architecture/fragments.md b/content/develop/concepts/architecture/fragments.md
index 680c47511..72710d962 100644
--- a/content/develop/concepts/architecture/fragments.md
+++ b/content/develop/concepts/architecture/fragments.md
@@ -115,11 +115,9 @@ slow_table()
When `parallel=True`, the behavior depends on the type of rerun:
-- **During a full-app rerun**, Streamlit dispatches the fragment to a thread pool. It runs concurrently with your other parallel fragments and with the rest of your main script, rather than blocking on each fragment in turn. If `slow_chart` and `slow_table` each take two seconds, running them in parallel lets your app finish in about two seconds instead of four.
+- During a full-app rerun, Streamlit dispatches the fragment to a thread pool. It runs concurrently with your other parallel fragments and with the rest of your main script, rather than blocking on each fragment in turn. If `slow_chart` and `slow_table` each take two seconds, running them in parallel lets your app finish in about two seconds instead of four.
-- **During a fragment rerun** (when a user interacts with a widget inside the fragment), execution stays sequential. The fragment runs by itself on the main thread, exactly like a non-parallel fragment. This keeps your state updates predictable when a user is actively interacting with a fragment.
-
-Parallel fragments are most helpful when each fragment does independent, time-consuming work. If your fragments are fast or depend on each other's results, running them in parallel adds complexity without a meaningful speedup.
+- During a fragment rerun (when a user interacts with a widget inside the fragment), execution stays sequential. The fragment runs by itself on the main thread, exactly like a non-parallel fragment. This keeps your state updates predictable when a user is actively interacting with a fragment.
### Restricted commands during parallel execution
@@ -129,7 +127,19 @@ Because parallel fragments run concurrently on separate threads during the initi
- [`st.switch_page`](/develop/api-reference/navigation/st.switch_page)
- Writing to containers created outside the fragment.
-These commands work normally during a fragment rerun (for example, after a user interacts with a widget inside the fragment), because fragment reruns are sequential. If you need one of these commands, call it from a non-parallel fragment or from the main body of your script.
+These commands work normally during a fragment rerun, even inside a parallel fragment, because fragment reruns are sequential. To use one of these commands inside a parallel fragment, gate it behind a widget interaction rather than calling it unconditionally:
+
+```python
+@st.fragment
+def my_fragment(parallel=True):
+ if st.button("Open dialog"):
+ # Safe: only called during a fragment rerun, after the user clicks
+ show_dialog()
+
+@st.dialog("My dialog")
+def show_dialog():
+ st.write("Hello!")
+```
### Thread safety and shared state
From efda696ba607b2e76d572058f2762f69c290da10 Mon Sep 17 00:00:00 2001
From: Patrick Plettner <17936703+pplettner@users.noreply.github.com>
Date: Tue, 7 Jul 2026 20:02:25 +0200
Subject: [PATCH 3/7] Add tutorial for parallel fragments
---
.../fragments/run-fragments-in-parallel.md | 195 ++++++++++++++++++
1 file changed, 195 insertions(+)
create mode 100644 content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md
diff --git a/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md b/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md
new file mode 100644
index 000000000..c07f36c7f
--- /dev/null
+++ b/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md
@@ -0,0 +1,195 @@
+---
+title: Speed up your app with parallel fragments
+slug: /develop/tutorials/execution-flow/run-fragments-in-parallel
+description: Learn how to run Streamlit fragments concurrently with parallel=True to load slow, independent operations like database queries and API calls at the same time.
+keywords: parallel fragments, st.fragment, parallel, concurrency, threading, performance, slow queries, api calls, execution flow, app performance
+---
+
+# Speed up your app with parallel fragments
+
+Streamlit lets you turn functions into [fragments](/develop/concepts/architecture/fragments), which can rerun independently from the full script. By default, fragments run one after another on the main thread. If your app has several slow, independent operations—like database queries or API calls—you can run them at the same time by setting `parallel=True` in the [`@st.fragment`](/develop/api-reference/execution-flow/st.fragment) decorator. During a full-app rerun, Streamlit dispatches each parallel fragment to a thread pool so they execute concurrently instead of blocking each other.
+
+## Applied concepts
+
+- Use `parallel=True` to run slow, independent fragments concurrently.
+- Store each fragment's results in Session State to safely combine them.
+
+## Prerequisites
+
+- This tutorial requires the following version of Streamlit:
+
+ ```text
+ streamlit>=1.58.0
+ ```
+
+- You should have a clean working directory called `your-repository`.
+- You should have a basic understanding of fragments and Session State.
+
+## Summary
+
+In this example, you'll build an app that loads data from three independent, slow sources. Without parallel fragments, the app waits for each source in turn, so the total load time is the sum of all three. By marking each source's fragment with `parallel=True`, the three loads run concurrently during a full-app rerun, and the app finishes in about the time of the slowest single source.
+
+You'll simulate slow work with `time.sleep`, but in a real app these fragments would run database queries, call external APIs, or perform other independent, time-consuming work.
+
+Here's a look at what you'll build:
+
+
+
+```python
+import streamlit as st
+import time
+import random
+
+
+def slow_load(source_name, seconds):
+ """Simulate a slow, independent data source."""
+ time.sleep(seconds)
+ return {"source": source_name, "value": random.randint(0, 100)}
+
+
+st.title("Parallel data loading")
+
+@st.fragment(parallel=True)
+def load_sales():
+ result = slow_load("Sales", 3)
+ st.session_state.sales = result
+ st.metric("Sales", result["value"])
+
+@st.fragment(parallel=True)
+def load_traffic():
+ result = slow_load("Traffic", 3)
+ st.session_state.traffic = result
+ st.metric("Traffic", result["value"])
+
+@st.fragment(parallel=True)
+def load_inventory():
+ result = slow_load("Inventory", 3)
+ st.session_state.inventory = result
+ st.metric("Inventory", result["value"])
+
+start = time.time()
+
+cols = st.columns(3)
+with cols[0]:
+ load_sales()
+with cols[1]:
+ load_traffic()
+with cols[2]:
+ load_inventory()
+```
+
+
+
+## Build the app
+
+### Initialize your app and a slow data source
+
+1. In `your-repository`, create a file named `app.py`.
+
+1. In a terminal, change directories to `your-repository`, and start your app.
+
+ ```bash
+ streamlit run app.py
+ ```
+
+ Your app will be blank because you still need to add code.
+
+1. In `app.py`, write the following:
+
+ ```python
+ import streamlit as st
+ import time
+ import random
+ ```
+
+ You'll use `time` to simulate slow work and `random` to generate sample values.
+
+1. Save your `app.py` file, and view your running app.
+
+1. In your app, select "**Always rerun**", or press the "**A**" key.
+
+ Your preview will be blank but will automatically update as you save changes to `app.py`.
+
+1. Return to your code.
+
+1. Define a helper function to simulate a slow, independent data source.
+
+ ```python
+ def slow_load(source_name, seconds):
+ """Simulate a slow, independent data source."""
+ time.sleep(seconds)
+ return {"source": source_name, "value": random.randint(0, 100)}
+ ```
+
+ In a real app, you'd replace the `time.sleep` call with a database query, an API call, or another time-consuming operation.
+
+1. Add a title to your app.
+
+ ```python
+ st.title("Parallel data loading")
+ ```
+
+### Define parallel fragments for each data source
+
+Each data source is independent, so each one is a good candidate for its own parallel fragment. Each fragment writes its result to its own Session State key, which keeps the fragments from interfering with each other when they run concurrently.
+
+1. Define a fragment to load your first source.
+
+ ```python
+ @st.fragment(parallel=True)
+ def load_sales():
+ result = slow_load("Sales", 3)
+ st.session_state.sales = result
+ st.metric("Sales", result["value"])
+ ```
+
+ The `parallel=True` argument tells Streamlit to dispatch this fragment to a thread pool during a full-app rerun so it can run at the same time as your other parallel fragments.
+
+1. Define fragments for your other two sources in the same way.
+
+ ```python
+ @st.fragment(parallel=True)
+ def load_traffic():
+ result = slow_load("Traffic", 3)
+ st.session_state.traffic = result
+ st.metric("Traffic", result["value"])
+
+
+ @st.fragment(parallel=True)
+ def load_inventory():
+ result = slow_load("Inventory", 3)
+ st.session_state.inventory = result
+ st.metric("Inventory", result["value"])
+ ```
+
+ Each fragment writes to a different Session State key (`sales`, `traffic`, and `inventory`). Because parallel fragments can run concurrently, having each one write to its own key avoids race conditions.
+
+### Call your fragments and measure the speedup
+
+1. Call each fragment in its own column.
+
+ ```python
+ start = time.time()
+
+ cols = st.columns(3)
+ with cols[0]:
+ load_sales()
+ with cols[1]:
+ load_traffic()
+ with cols[2]:
+ load_inventory()
+ ```
+
+ Each fragment renders its metric into its own column. Because the columns are created in the main body of the script, each fragment writes only into its own main body.
+
+1. Save your `app.py` file, and view your running app.
+
+ Each fragment sleeps for three seconds, but because they run in parallel, your app loads in about three seconds total instead of nine. Try changing `parallel=True` to `parallel=False` (or removing it) to see the difference: the app will take about nine seconds because the fragments run one after another.
+
+## Next steps
+
+Replace the `slow_load` helper with your own slow operations, such as database queries or API calls. To learn more about parallel execution, including command restrictions and thread safety, see [Run fragments in parallel](/develop/concepts/architecture/fragments#run-fragments-in-parallel).
+
+```
+
+```
From bbd256831b5d9f7091cd125ced87ac3c2fab394a Mon Sep 17 00:00:00 2001
From: Patrick Plettner <17936703+pplettner@users.noreply.github.com>
Date: Tue, 7 Jul 2026 20:05:26 +0200
Subject: [PATCH 4/7] Add tutorial for parallel fragments
---
.../execution-flow/fragments/run-fragments-in-parallel.md | 4 ----
1 file changed, 4 deletions(-)
diff --git a/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md b/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md
index c07f36c7f..fb077e6a3 100644
--- a/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md
+++ b/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md
@@ -189,7 +189,3 @@ Each data source is independent, so each one is a good candidate for its own par
## Next steps
Replace the `slow_load` helper with your own slow operations, such as database queries or API calls. To learn more about parallel execution, including command restrictions and thread safety, see [Run fragments in parallel](/develop/concepts/architecture/fragments#run-fragments-in-parallel).
-
-```
-
-```
From fee1e164713d3ab6a4adcdea9fc3c2111311a040 Mon Sep 17 00:00:00 2001
From: Patrick Plettner <17936703+pplettner@users.noreply.github.com>
Date: Thu, 9 Jul 2026 15:31:04 +0200
Subject: [PATCH 5/7] Add parallel fragments tutorial in menu
---
content/menu.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/content/menu.md b/content/menu.md
index d8f6a415c..6625c47ff 100644
--- a/content/menu.md
+++ b/content/menu.md
@@ -691,6 +691,8 @@ site_menu:
url: /develop/tutorials/execution-flow/create-a-multiple-container-fragment
- category: Develop / Tutorials / Execution flow / Start and stop a streaming fragment
url: /develop/tutorials/execution-flow/start-and-stop-fragment-auto-reruns
+ - category: Develop / Tutorials / Execution flow / Run fragments in parallel
+ url: /develop/tutorials/execution-flow/run-fragments-in-parallel
- category: Develop / Tutorials / Build custom components
url: /develop/tutorials/custom-components
- category: Develop / Tutorials / Build custom components / Create a component with Pure TypeScript
From d223868d4251d04b8097cb8a184cc435854db235 Mon Sep 17 00:00:00 2001
From: Patrick Plettner <17936703+pplettner@users.noreply.github.com>
Date: Thu, 9 Jul 2026 16:56:03 +0200
Subject: [PATCH 6/7] Add parallel fragments tutorial on tutorial entry page
---
content/develop/tutorials/execution-flow/_index.md | 8 ++++++++
.../execution-flow/fragments/run-fragments-in-parallel.md | 4 ----
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/content/develop/tutorials/execution-flow/_index.md b/content/develop/tutorials/execution-flow/_index.md
index 7396c8b2c..e750b7cc9 100644
--- a/content/develop/tutorials/execution-flow/_index.md
+++ b/content/develop/tutorials/execution-flow/_index.md
@@ -35,4 +35,12 @@ Use a fragment to live-stream data. Use a button to start and stop the live-stre
+
+
+Speed up your app with parallel fragments
+
+Set up fragments with long load times to run concurrently.
+
+
+
diff --git a/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md b/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md
index fb077e6a3..20b0f62b0 100644
--- a/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md
+++ b/content/develop/tutorials/execution-flow/fragments/run-fragments-in-parallel.md
@@ -67,8 +67,6 @@ def load_inventory():
st.session_state.inventory = result
st.metric("Inventory", result["value"])
-start = time.time()
-
cols = st.columns(3)
with cols[0]:
load_sales()
@@ -169,8 +167,6 @@ Each data source is independent, so each one is a good candidate for its own par
1. Call each fragment in its own column.
```python
- start = time.time()
-
cols = st.columns(3)
with cols[0]:
load_sales()
From 165ab3d7227b9283ff1fb7c7704ec0024ec0bf1d Mon Sep 17 00:00:00 2001
From: Patrick Plettner <17936703+pplettner@users.noreply.github.com>
Date: Thu, 9 Jul 2026 17:07:01 +0200
Subject: [PATCH 7/7] Correct menu hierarchy
---
content/menu.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/content/menu.md b/content/menu.md
index 6625c47ff..8bb2c969c 100644
--- a/content/menu.md
+++ b/content/menu.md
@@ -691,7 +691,7 @@ site_menu:
url: /develop/tutorials/execution-flow/create-a-multiple-container-fragment
- category: Develop / Tutorials / Execution flow / Start and stop a streaming fragment
url: /develop/tutorials/execution-flow/start-and-stop-fragment-auto-reruns
- - category: Develop / Tutorials / Execution flow / Run fragments in parallel
+ - category: Develop / Tutorials / Execution flow / Speed up your app with parallel fragments
url: /develop/tutorials/execution-flow/run-fragments-in-parallel
- category: Develop / Tutorials / Build custom components
url: /develop/tutorials/custom-components