Streamlit Development

2025 Guide: Capture Streamlit Grid Layout in 3 Steps

Tired of basic Streamlit layouts? Our 2025 guide shows you how to create stunning, responsive grid layouts in just 3 easy steps. Level up your app's UI today!

A

Alexei Petrov

Senior Data Scientist and Python enthusiast specializing in building interactive web applications.

7 min read30 views

2025 Guide: Capture Streamlit Grid Layout in 3 Steps

Streamlit is a game-changer for turning data scripts into shareable web apps with breathtaking speed. But let's be honest—making those apps look really polished and professional can sometimes feel like a puzzle. If you've ever felt constrained by the default top-to-bottom layout, you're in the right place. This guide will show you how to break free and create sophisticated, responsive grid layouts in just three simple steps.

Why Your Streamlit App Deserves a Better Layout

Before we dive into the 'how,' let's talk about the 'why.' A good layout isn't just about making things pretty; it's about crafting a better user experience and communicating your insights more effectively. A well-structured grid can:

  • Improve Information Density: Display more charts, metrics, and controls on a single screen without it feeling cluttered. This is crucial for complex dashboards where context is key.
  • Guide the User's Eye: Arrange elements logically to tell a clear story. You can place the most important KPIs at the top, followed by detailed charts, creating a natural flow of information.
  • Boost Professionalism: A custom layout instantly elevates your app from a simple script to a polished, credible tool. It shows attention to detail and a commitment to user experience.

By moving beyond the default linear flow, you transform your app from a document into a dynamic, interactive dashboard.

Step 1: The Foundation with st.columns()

The first tool in any Streamlit developer's layout toolkit is st.columns(). It's the built-in, officially supported way to place elements side-by-side, and it's perfect for simple arrangements.

How It Works

The concept is straightforward: you call st.columns() and it returns a list of column objects. You can then use these objects as contexts (with the with statement) to populate them with content.

You can specify the number of equal-width columns:

# Creates three columns of equal width
col1, col2, col3 = st.columns(3)

Or you can pass a list of numbers or floats to specify their relative widths:

# Creates two columns, the first being three times wider than the second
col1, col2 = st.columns([3, 1])

A Simple Example

Let's create a simple dashboard header with a title and a key metric.

import streamlit as st
import pandas as pd
import numpy as np

st.set_page_config(layout="wide")

st.title("Sales Dashboard")

# Create two columns
col1, col2 = st.columns([3, 1])

with col1:
    st.header("Quarterly Performance")
    chart_data = pd.DataFrame(
        np.random.randn(20, 3),
        columns=["Sales", "Profit", "Marketing Spend"]
    )
    st.line_chart(chart_data)

with col2:
    st.metric(label="Total Sales", value="$1.2M", delta="12%")
    st.metric(label="Customer Satisfaction", value="98%", delta="2%")
    st.write("Key metrics for Q3 2025.")

This is clean, easy, and works great for many scenarios. However, it has its limits.

Advertisement

The Limitations

st.columns() is fundamentally row-based. You define a row, you fill it, and then you move to the next. This makes it difficult to:

  • Create a true grid where items can span multiple rows or columns.
  • Build complex masonry-style layouts.
  • Ensure consistent spacing and alignment in a more intricate design.
  • Natively handle responsiveness (e.g., stacking columns on mobile) without complex logic.

For that, we need to bring in the big guns.

Step 2: Unleash True Power with Containers and CSS Grid

This is where your Streamlit app's UI graduates from good to great. By combining Streamlit's st.container() with the power of custom CSS, you unlock the same layout capabilities that power the modern web.

The st.container() Primitive

Think of st.container() as an empty box or a <div> in HTML. It doesn't do anything on its own, but it allows you to group a set of elements. We can then target this box with CSS to control the layout of everything inside it.

Injecting Custom CSS

To apply our styles, we need a way to add CSS to our Streamlit app. The safest way is to wrap it in a <style> tag and pass it to st.markdown() with the `unsafe_allow_html=True` flag.

def local_css(file_name):
    with open(file_name) as f:
        st.markdown(f"", unsafe_allow_html=True)

# In your main app file:
# local_css("style.css")

Now, we can create a `style.css` file and put all our layout rules there.

Building a CSS Grid Layout

Let's build a 2x2 grid for four metrics cards. First, our CSS in `style.css`:

/* Define our grid container */
.grid-container {
    display: grid;
    grid-template-columns: 1fr 1fr; /* Two equal-width columns */
    gap: 20px; /* Space between grid items */
}

/* Style for the items inside the grid (optional) */
.grid-item {
    background-color: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 10px;
    padding: 20px;
}

Now, let's use this in our Python script. We'll use a little HTML trick inside a `st.markdown` call to apply our class to a container.

import streamlit as st

# Assume local_css("style.css") has been called

st.header("Key Performance Indicators")

# We use markdown to inject a div with our custom class
st.markdown('
', unsafe_allow_html=True) # --- Grid Item 1 --- with st.container(): st.markdown('
', unsafe_allow_html=True) st.metric(label="Revenue", value="$450K", delta="5.2%") st.markdown('
', unsafe_allow_html=True) # --- Grid Item 2 --- with st.container(): st.markdown('
', unsafe_allow_html=True) st.metric(label="New Users", value="1,230", delta="-2.1%") st.markdown('
', unsafe_allow_html=True) # --- Grid Item 3 --- with st.container(): st.markdown('
', unsafe_allow_html=True) st.metric(label="Engagement", value="78%", delta="1.5%") st.markdown('
', unsafe_allow_html=True) # --- Grid Item 4 --- with st.container(): st.markdown('
', unsafe_allow_html=True) st.metric(label="Churn Rate", value="1.8%", delta="-0.3%", delta_color="inverse") st.markdown('
', unsafe_allow_html=True) # Close the grid container div st.markdown('
', unsafe_allow_html=True)

While this approach using `st.markdown` to wrap `st.container` feels a bit clunky, it's currently the most reliable way to apply a CSS class to a block of Streamlit elements. The result is a perfect 2x2 grid that is far more robust than nested `st.columns()` calls.

Step 3: Go Pro with Responsive & Reusable Grids

A static grid is good, but a dynamic, responsive grid is professional. A great layout should adapt to the user's screen, whether it's a wide monitor or a mobile phone.

Making It Responsive with Media Queries

CSS media queries are the key. Let's update our `style.css` to make our 2x2 grid stack into a single column on smaller screens.

/* Existing styles from Step 2... */
.grid-container {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
}

.grid-item { ... }

/* Media query for mobile devices */
@media (max-width: 768px) {
    .grid-container {
        grid-template-columns: 1fr; /* Stack to a single column */
    }
}

That's it! With this simple addition, your layout will automatically reflow when the browser window is narrower than 768px. This is something that's very difficult to achieve cleanly with `st.columns()` alone.

Creating Reusable Layout Components

To keep your code DRY (Don't Repeat Yourself), you can wrap your layout patterns in functions. For example, you can create a function that generates a styled metric card.

def styled_metric(label, value, delta):
    st.markdown('
', unsafe_allow_html=True) st.metric(label=label, value=value, delta=delta) st.markdown('
', unsafe_allow_html=True) # ... later in your app ... st.markdown('
', unsafe_allow_html=True) styled_metric("Revenue", "$450K", "5.2%") styled_metric("New Users", "1,230", "-2.1%") # ... etc. st.markdown('
', unsafe_allow_html=True)

This abstraction makes your main app script much cleaner and easier to read, separating the content from the layout implementation.

st.columns vs. CSS Grid: A Quick Comparison

When should you use which? Here's a quick breakdown to help you decide.

Featurest.columns()st.container() + CSS Grid
Ease of UseExtremely HighMedium
FlexibilityLow (Row-based, fixed structure)Very High (Full control over rows, columns, spanning)
ResponsivenessManual & ClunkyNative & Powerful (via Media Queries)
ComplexityMinimal (Pure Python)Moderate (Requires HTML/CSS knowledge)
Best ForSimple side-by-side elements, basic dashboards.Complex dashboards, card layouts, professional UIs.

Key Takeaways for Your Next Project

You now have the tools to build virtually any layout you can imagine in Streamlit. Here are the key points to remember:

  • Start Simple: For basic two or three-column layouts, st.columns() is your best friend. It's fast, easy, and requires zero extra setup.
  • Embrace CSS for Power: When you need more control, a true grid, or complex arrangements, don't hesitate to use st.container() and inject custom CSS. It's the path to a truly professional-looking application.
  • Design for All Screens: Always add media queries to your CSS to ensure your app looks great on both desktop and mobile. A responsive design is a hallmark of a high-quality web application.

By mastering these three steps, you can confidently design and build Streamlit apps that are not only functional but also beautifully designed and a joy to use. Happy coding!

Tags

You May Also Like