Create a fragment across multiple containers
Streamlit lets you turn functions into fragments, which can rerun independently from the full script. If your fragment doesn't write to outside containers, Streamlit will clear and redraw all the fragment elements with each fragment rerun. However, if your fragment does write elements to outside containers, Streamlit will not clear those elements during a fragment rerun. Instead, those elements accumulate with each fragment rerun until the next full-script rerun. If you want a fragment to update multiple containers in your app, use st.empty()
containers to prevent accumulating elements.
Applied concepts
- Use fragments to run two independent processes separately.
- Distribute a fragment across multiple containers.
Prerequisites
-
The following must be installed in your Python environment:
streamlit>=1.37.0
-
You should have a clean working directory called
your-repository
. -
You should have a basic understanding of fragments and
st.empty()
.
Summary
In this toy example, you'll build an app with six containers. Three containers will have orange cats. The other three containers will have black cats. You'll have three buttons in the sidebar: "Herd the black cats," "Herd the orange cats," and "Herd all the cats." Since herding cats is slow, you'll use fragments to help Streamlit run the associated processes efficiently. You'll create two fragments, one for the black cats and one for the orange cats. Since the buttons will be in the sidebar and the fragments will update containers in the main body, you'll use a trick with st.empty()
to ensure you don't end up with too many cats in your app (if it's even possible to have too many cats). π»
Here's a look at what you'll build:
import streamlit as st
import time
st.title("Cats!")
row1 = st.columns(3)
row2 = st.columns(3)
grid = [col.container(height=200) for col in row1 + row2]
safe_grid = [card.empty() for card in grid]
def black_cats():
time.sleep(1)
st.title("πββ¬ πββ¬")
st.markdown("πΎ πΎ πΎ πΎ")
def orange_cats():
time.sleep(1)
st.title("π π")
st.markdown("πΎ πΎ πΎ πΎ")
@st.fragment
def herd_black_cats(card_a, card_b, card_c):
st.button("Herd the black cats")
container_a = card_a.container()
container_b = card_b.container()
container_c = card_c.container()
with container_a:
black_cats()
with container_b:
black_cats()
with container_c:
black_cats()
@st.fragment
def herd_orange_cats(card_a, card_b, card_c):
st.button("Herd the orange cats")
container_a = card_a.container()
container_b = card_b.container()
container_c = card_c.container()
with container_a:
orange_cats()
with container_b:
orange_cats()
with container_c:
orange_cats()
with st.sidebar:
herd_black_cats(grid[0].empty(), grid[2].empty(), grid[4].empty())
herd_orange_cats(grid[1].empty(), grid[3].empty(), grid[5].empty())
st.button("Herd all the cats")
Build the example
Initialize your app
-
In
your_repository
, create a file namedapp.py
. -
In a terminal, change directories to
your_repository
and start your app.streamlit run app.py
Your app will be blank since you still need to add code.
-
In
app.py
, write the following:import streamlit as st import time
You'll use
time.sleep()
to slow things down and see the fragments working. -
Save your
app.py
file and view your running app. -
Click "Always rerun" or hit your "A" key in your running app.
Your running preview will automatically update as you save changes to
app.py
. Your preview will still be blank. Return to your code.
Frame out your app's containers
-
Add a title to your app and two rows of three containers.
st.title("Cats!") row1 = st.columns(3) row2 = st.columns(3) grid = [col.container(height=200) for col in row1 + row2]
Save your file to see your updated preview.
-
Define a helper function to draw two black cats.
def black_cats(): time.sleep(1) st.title("πββ¬ πββ¬") st.markdown("πΎ πΎ πΎ πΎ")
This function represents "herding two cats" and uses
time.sleep()
to simulate a slower process. You will use this to draw two cats in one of your grid cards later on. -
Define another helper function to draw two orange cats.
def orange_cats(): time.sleep(1) st.title("π π") st.markdown("πΎ πΎ πΎ πΎ")
-
Optional: Test out your functions by calling each one within a grid card.
with grid[0]: black_cats() with grid[1]: orange_cats()
Save your
app.py
file to see the preview. Delete these four lines when finished.
Define your fragments
Since each fragment will span across the sidebar and three additional containers, you'll use the sidebar to hold the main body of the fragment and pass the three containers as function arguments.
-
Use an
@st.fragment
decorator and start your black-cat fragment definition.@st.fragment def herd_black_cats(card_a, card_b, card_c):
-
Add a button for rerunning this fragment.
st.button("Herd the black cats")
-
Write to each container using your helper function.
with card_a: black_cats() with card_b: black_cats() with card_c: black_cats()
This code above will not behave as desired, but you'll explore and correct this in the following steps.
-
Test out your code. Call your fragment function in the sidebar.
with st.sidebar: herd_black_cats(grid[0], grid[2], grid[4])
Save your file and try using the button in the sidebar. More and more cats are appear in the cards with each fragment rerun! This is the expected behavior when fragments write to outside containers. To fix this, you will pass
st.empty()
containers to your fragment function. -
Delete the lines of code from the previous two steps.
-
To prepare for using
st.empty()
containers, correct your cat-herding function as follows. After the button, define containers to place in thest.empty()
cards you'll pass to your fragment.container_a = card_a.container() container_b = card_b.container() container_c = card_c.container() with container_a: black_cats() with container_b: black_cats() with container_c: black_cats()
In this new version,
card_a
,card_b
, andcard_c
will best.empty()
containers. You createcontainer_a
,container_b
, andcontainer_c
to allow Streamlit to draw multiple elements on each grid card. -
Similarly define your orange-cat fragment function.
@st.fragment def herd_orange_cats(card_a, card_b, card_c): st.button("Herd the orange cats") container_a = card_a.container() container_b = card_b.container() container_c = card_c.container() with container_a: orange_cats() with container_b: orange_cats() with container_c: orange_cats()
Put the functions together together to create an app
-
Call both of your fragments in the sidebar.
with st.sidebar: herd_black_cats(grid[0].empty(), grid[2].empty(), grid[4].empty()) herd_orange_cats(grid[1].empty(), grid[3].empty(), grid[5].empty())
By creating
st.empty()
containers in each card and passing them to your fragments, you prevent elements from accumulating in the cards with each fragment rerun. If you create thest.empty()
containers earlier in your app, full-script reruns will clear the orange-cat cards while (first) rendering the black-cat cards. -
Include a button outside of your fragments. When clicked, the button will trigger a full-script rerun since you're calling its widget function outside of any fragment.
st.button("Herd all the cats")
-
Save your file and try out the app! When you click "Herd the black cats" or "Herd the orange cats," Streamlit will only redraw the three related cards. When you click "Herd all the cats," Streamlit redraws all six cards.
Still have questions?
Our forums are full of helpful information and Streamlit experts.