- Contents
- Summary
- Writing into containers
- Basic containers
- st.sidebar
- st.container
- st.columns
- Collapsible and overlay containers
- st.expander
- st.tabs
- st.popover
- Placeholder containers
- st.empty
- Flex layouts
- Horizontal containers
- Alignment
- Gap and spacing
- Fixed-height and scrollable containers
- Dynamic containers
- Tracking open and closed state
- Using callbacks
- Programmatic control
- Nesting containers
Using layouts and containers
By default, Streamlit renders elements top-to-bottom in the order they appear in your script. Containers let you group elements, arrange them side by side, hide them behind tabs or expanders, and control alignment and spacing. This guide walks through Streamlit's layout tools from the basics to advanced patterns.
Summary
- Use
st.sidebarfor persistent controls that stay visible across your app. - Use
st.columnsto place elements side by side. - Use
st.tabs,st.expander, andst.popoverto organize content behind collapsible or overlay containers. - Use
st.containerto group elements and control display order independently from script order. - Use
st.emptyto create a single-element placeholder that can be replaced or cleared. - Horizontal containers, gap, alignment, and
st.spacegive you fine-grained control over flex layouts. st.tabs,st.expander, andst.popovercan track their open/closed state and trigger reruns when you seton_change.
Writing into containers
There are two ways to add elements to a container: context managers and method calls.
With a context manager (with), everything inside the block is written to the container:
With method calls, you call Streamlit commands directly on the container object:
Method calls are especially useful when you need to write into a container that was created earlier in your script, letting you display elements in a different order than the script executes. The previous two examples produce the same visual result.
Basic containers
st.sidebar
The sidebar is a persistent panel on the left side of your app. It's ideal for controls and filters that should stay visible while the user scrolls through the main content.
You can also use the context manager syntax:
st.container
A plain container groups elements together. On its own, a container is invisible — it doesn't add any visual boundary. Its primary purpose is letting you write elements out of order. In the following example, the empty container is drawn first, and then text is drawn after it. Finally, text is drawn inside it, appearing before the previously drawn text.
You can add a visible border with border=True:
st.columns
Columns are the simplest way to place elements side by side. Each column gets a fixed share of the available width. Pass the number of equal columns or a list of relative widths:
For unequal widths, pass a list:
Tip
Columns are great for quick grid-like layouts, but they are not as adaptive as horizontal containers. If the screen width is too narrow, the columns will stack instead of flex wrapping. For more control over how elements flow and wrap, see Horizontal containers in the flex layouts section below.
Collapsible and overlay containers
st.expander
An expander hides content behind a collapsible header. It's useful for secondary details, help text, or advanced options:
st.tabs
Tabs organize content into labeled views. Only one tab is visible at a time, but by default all tab content runs on every rerun:
st.popover
A popover displays content in a floating overlay triggered by a button. It's useful for settings or filters that shouldn't take up permanent space:
Placeholder containers
st.empty
st.empty creates a single-element placeholder. Each time you write to it, the previous content is replaced:
To replace multiple elements at once, nest a st.container inside st.empty:
st.empty is the primary tool for updating your app's display in place. For more about updating and replacing elements, see Update and replace elements.
Flex layouts
Horizontal containers
Set horizontal=True on st.container to lay out its children in a horizontal row. Unlike columns, elements in a horizontal container size themselves based on their content and wrap to the next line when they overflow:
Horizontal containers are generally preferred over st.columns for side-by-side layouts because they adapt naturally to their content. Columns divide the available width into fixed proportions, which works well for simple grids but can waste space or cause awkward sizing when elements vary in width. Horizontal containers avoid this by letting each element take only the space it needs.
For example, a row of inputs with a submit button works well in a horizontal container without worrying about proportions:
Alignment
Containers and columns support vertical and horizontal alignment:
For more information about alignment, see the st.container API reference.
Gap and spacing
Columns and containers accept a gap parameter to control spacing between child elements. Valid sizes range from "xxsmall" through "xxlarge":
For manual spacing, use st.space:
Fixed-height and scrollable containers
Set height on a container to a pixel value to create a scrollable region:
Dynamic containers
Note
Dynamic containers were introduced in v1.55.0. When on_change is set, containers become widget-like. This means that they track state, accept callbacks, and support keys. If you're unfamiliar with how Streamlit widgets manage state and identity, see Widget behavior first.
By default, st.tabs, st.expander, and st.popover are static: all of their content runs on every rerun regardless of whether they are open or closed. You can change this with the on_change parameter, which enables state tracking and reruns.
Tracking open and closed state
Set on_change="rerun" to make a container track its state. The .open attribute on the returned container object tells you whether the container is currently open. This enables lazy loading of content in tabs and expanders.
Using callbacks
Pass a callable to on_change to run a function when the user opens, closes, or switches containers. If you need to access the container's state in the callback, use a key and retrieve the state from st.session_state.
Programmatic control
When you provide a key to a state-tracking container, you can manipulate the container's state through st.session_state:
Nesting containers
You can nest containers inside each other. For example, columns inside tabs or expanders inside columns:
Still have questions?
Our forums are full of helpful information and Streamlit experts.
