Skip to content

How to Build Streamlit Layouts with st.columns

Updated on

Streamlit columns are the main way to place elements side by side in a Streamlit app.

What Streamlit columns mean

If a Streamlit page feels too long, too linear, or too hard to scan, columns are often the first layout tool to reach for.

st.columns lets you split one horizontal section of the page into multiple containers. Each container can hold its own widgets, charts, metrics, tables, or explanatory text.

In plain terms, columns help a Streamlit app stop feeling like one long vertical notebook. They let you arrange related information next to each other so the user can compare, act, or understand faster.

Why st.columns matters

Columns matter because layout changes how quickly a reader understands the page.

The same data can feel:

  • cluttered in one long column
  • clear when metrics, controls, and outputs are grouped side by side

That is why columns often improve not just appearance, but also usability.

In real apps, they help with:

  • dashboard-style summaries
  • side-by-side comparisons
  • form and output layouts
  • action rows with buttons
  • file upload plus preview flows

What st.columns is actually for

The point of columns is not to make the page "fancier." The point is to organize attention.

Use columns when you want the user to:

  • compare two charts or two tables
  • see filters beside results
  • scan several metrics in one row
  • keep related actions grouped together

After reading this guide, you should be able to:

  • build simple and weighted column layouts
  • use modern column options such as gap, vertical_alignment, border, and width
  • choose between columns, sidebar, tabs, and containers more confidently
  • avoid the most common layout mistakes in Streamlit

Quick Start

This first example shows the basic idea: create columns, then place content inside each one.

import streamlit as st
 
col1, col2 = st.columns(2)
 
with col1:
    st.metric("Revenue", "$128K")
 
with col2:
    st.metric("Growth", "12.4%")

This is the most common use case for st.columns: putting related items on one row instead of stacking them vertically.

Use width ratios when the content is uneven

Not every layout should use equal-width columns. If one side needs more space, pass a list of ratios.

import streamlit as st
 
chart_col, notes_col = st.columns([3, 1])
 
with chart_col:
    st.write("Main chart area")
 
with notes_col:
    st.write("Key notes and filters")

That pattern is useful when one column holds a chart or dataframe and the other holds lighter supporting content.

Core arguments you should know

ArgumentWhat it doesWhen to care
specSets the number of columns or their relative widthsAlways
gapControls the space between columnsUseful for density and readability
vertical_alignmentAligns content to the top, center, or bottomHelpful for mixed-height content
borderAdds borders around columnsUseful for card-like sections
widthControls the overall row widthHelpful in constrained layouts

A modern st.columns example

This example uses current layout options to build a cleaner action-and-results row.

import streamlit as st
 
col1, col2 = st.columns(
    [1, 2],
    gap="medium",
    vertical_alignment="top",
    border=True,
)
 
with col1:
    st.subheader("Controls")
    st.selectbox("Region", ["APAC", "EMEA", "LATAM", "North America"])
    st.button("Refresh", type="primary", width="stretch")
 
with col2:
    st.subheader("Results")
    st.write("Place your chart, table, or summary here")

This kind of layout works well because it separates "decide" from "review" without forcing the user to scroll up and down.

Common column patterns in real apps

Metrics row

Columns are often the easiest way to show a small dashboard summary at the top of a page.

import streamlit as st
 
col1, col2, col3 = st.columns(3, gap="small")
 
with col1:
    st.metric("Orders", "1,245")
with col2:
    st.metric("Conversion", "5.2%")
with col3:
    st.metric("Refund rate", "1.1%")

Upload plus preview

This pattern is useful when users should provide a file and immediately see what was loaded.

import pandas as pd
import streamlit as st
 
upload_col, preview_col = st.columns([1, 2], gap="medium")
 
with upload_col:
    uploaded_file = st.file_uploader("Upload CSV", type=["csv"])
 
with preview_col:
    if uploaded_file is not None:
        df = pd.read_csv(uploaded_file)
        st.dataframe(df.head(), width="stretch")

This is a common pattern for internal tools because it keeps the input and the feedback in one visual unit.

Controls beside results

If filters should stay visible while the user reads the output, a two-column layout is often more comfortable than pushing controls above a long result block.

That pattern works especially well when the right side contains a st.dataframe, chart, or summary card.

Layout plus exploratory analytics

If the real goal is not just "put two things side by side" but "put controls beside a rich exploratory data surface," PyGWalker can fit naturally into a Streamlit layout.

For example, one column can hold filters and notes, while the other holds a PyGWalker exploration view for the current dataframe.

See:

Action rows

If the page has paired actions such as Back/Next or Cancel/Save, columns give those actions a clean, predictable structure.

This is especially helpful when combined with st.button and st.session_state.

Columns vs other Streamlit layout tools

NeedBetter tool
Put elements side by side in the main pagest.columns
Keep global controls separate from the main pagest.sidebar
Group several elements without changing horizontal layoutst.container
Split large areas of content into switchable sectionsst.tabs

Columns are the right answer when the user benefits from seeing multiple things at once.

Practical layout advice

Keep the row simple

Two or three columns are usually easier to read than a row packed with many narrow sections.

Match layout to task complexity

If the content is dense or text-heavy, a single column may still be the better choice. Layout should help the content, not fight it.

Avoid deep nesting

Streamlit recommends not nesting columns more than once. In practice, deeper nesting usually makes the layout brittle and harder to read.

Use columns for comparison, not decoration

The strongest use cases are comparison, workflow grouping, and scannability. If the layout does not improve understanding, a simpler structure is usually better.

Troubleshooting

Why do my columns feel too cramped?

You may have too many columns for the amount of content on the page. Reduce the number of columns, widen the main one with ratios, or move global controls to the sidebar.

How do I align widgets that have different heights?

Use vertical_alignment when creating the columns, and keep the content inside each column structurally similar where possible.

Can I put buttons, uploads, and charts inside columns?

Yes. Columns are just containers, so they work with widgets, charts, text, tables, and media.

Why does nested layout become hard to manage?

Because each extra nesting level makes the page harder to reason about and more fragile on smaller screens. Keep nesting shallow.

Related Guides

Frequently Asked Questions

What does st.columns do in simple terms?

It creates side-by-side containers so you can place Streamlit elements on the same row.

How do I make one Streamlit column wider than another?

Pass a list of relative widths, such as st.columns([3, 1]).

When should I use columns in Streamlit?

Use columns when the user should compare items side by side or keep controls and results visible together.

How do I build a Streamlit layout?

Use layout primitives such as st.columns, st.sidebar, st.container, and st.tabs. st.columns is usually the main tool when the layout needs side-by-side sections.

Can I put widgets inside Streamlit columns?

Yes. You can place widgets, charts, text, metrics, and tables inside each column.

Should I nest Streamlit columns deeply?

Usually no. Streamlit recommends keeping nesting shallow because deep nesting makes layouts harder to manage and read.