DevHireLab
Tutorials
BootcampProblemsCode SimulatorAI InterviewSoonContact
DevHireLab
Tutorials
BootcampProblemsCode SimulatorAI InterviewSoonContact
01
Introduction to Matplotlib
02
Line, Bar, and Scatter Plots in Matplotlib
03
Introduction to Seaborn for Statistical Plots
04
Heatmaps, Pair Plots, and Distribution Plots in Seaborn
05
Interactive Visualizations with Plotly
06
Building Dashboards with Streamlit (Python-Only)
07
Data Storytelling: Choosing the Right Chart

Building Dashboards with Streamlit (Python-Only)

streamlit dashboard tutorial content is everywhere, but the single concept that actually confuses newcomers — Streamlit's top-to-bottom rerun execution model — rarely gets the dedicated explanation it needs. This streamlit python guide covers that model directly, plus the specific, commonly-hit pitfalls (caching, st.set_page_config ordering) that separate a genuinely helpful guide from a shallow widget list.

A Dashboard Without Touching HTML, CSS, or JavaScript

What Streamlit actually removes from the process

With Streamlit, there's no need to navigate a web app's front-end framework jungle, or wrestle with HTML, CSS, and JavaScript to showcase data and insights — the entire dashboard is written in plain Python, top to bottom.

Why Streamlit specifically, versus a custom web app

For most data dashboard use cases, Streamlit is genuinely the right choice — its caching system, wide range of built-in widgets, and free Community Cloud deployment option together make it the fastest realistic path from a Python script to a shareable, interactive web application, without needing separate front-end expertise at all.

What this article covers

The rerun execution model (the genuine foundation everything else builds on), core widgets and layout, integrating pandas and Plotly directly, and the specific pitfalls that trip up nearly every Streamlit beginner at least once.

The Core Mental Model: Streamlit Reruns Your Script Top-to-Bottom

The single concept worth understanding first

This is worth understanding thoroughly before writing a single widget: Streamlit reruns the entire script, top to bottom, on every single interaction — clicking a button, moving a slider, changing a dropdown. This sounds inefficient at first, but it's actually genuinely liberating: you write plain, top-down, imperative Python — no callbacks, no event handlers, no manually-managed state machines, which is exactly the tangled complexity traditional web frameworks require you to manage directly.

import streamlit as st

st.title("My Dashboard")
name = st.text_input("What's your name?")
st.write(f"Hello, {name}!")

Every time the text input changes, this entire script reruns from the top — st.title() executes again, st.text_input() executes again (now returning the new value), and st.write() executes again with the updated greeting. There's no explicit "on change" handler anywhere — the rerun-everything model is the event handling mechanism.

Why this matters practically for structuring an app

Because a slider, checkbox, or dropdown change triggers the entire script to run again from the very top, any genuinely expensive computation — loading a large CSV, running a machine learning model, querying a database — needs explicit caching. Without it, that expensive operation would re-run on every single interaction, even ones completely unrelated to that specific computation, making the app feel sluggish and unresponsive.

@st.cache_data: the fix
import pandas as pd
import streamlit as st

@st.cache_data
def load_data():
    return pd.read_csv("large_dataset.csv")

df = load_data()

Decorating a data-loading (or otherwise expensive) function with @st.cache_data tells Streamlit to only actually re-execute that function when its inputs genuinely change — on every other rerun, Streamlit reuses the previously computed, cached result instantly, rather than reloading the entire CSV from disk again. This single decorator is what keeps a Streamlit app responsive despite the rerun-everything model underlying the entire framework.

Building the Basic Layout: Title, Sidebar, and Filters

The skeleton of any dashboard
import streamlit as st

st.set_page_config(page_title="Sales Dashboard", layout="wide")
st.title("Sales Dashboard")
st.write("An overview of regional sales performance.")

st.set_page_config() sets page-level configuration — the browser tab's title, and the overall page layout ("wide" uses the full browser width, rather than a narrower, centered default). st.title() and st.write() handle basic text and headers — the essential skeleton every dashboard starts from.

Sidebar filters
import pandas as pd

df = pd.DataFrame({
    "species": ["Setosa", "Versicolor", "Virginica"] * 10,
    "petal_length": [1.4, 4.5, 5.9] * 10,
})

selected_species = st.sidebar.multiselect(
    "Select species", optidf["species"].unique(), default=df["species"].unique()
)

filtered_df = df[df["species"].isin(selected_species)]

st.sidebar holds filter widgets — st.selectbox(), st.multiselect(), st.slider(), and similar — separately from the main content area, keeping filters visually distinct from the actual data and charts they control. Applying the selected filter values directly to a pandas DataFrame, exactly as shown here (df[df["species"].isin(selected_species)]), ties directly back to the boolean filtering covered in the earlier pandas data selection article — Streamlit widgets simply provide the interactive front-end; the actual filtering logic underneath is ordinary pandas you already know.

Arranging elements side by side with columns
col1, col2 = st.columns(2)

with col1:
    st.metric("Total Sales", "$45,231")

with col2:
    st.metric("Average Order", "$127")

st.columns() arranges elements side by side, rather than Streamlit's vertical, stacked default — genuinely useful both for placing something like a logo image next to a title, and for laying out multiple charts or metrics in a row rather than one long, scrolling column.

Displaying Data and Interactive Charts

Showing tabular data
st.dataframe(filtered_df)
st.dataframe(filtered_df.describe())

st.dataframe() shows tabular data directly, automatically interactive — built-in column sorting and scrolling, with zero extra code required beyond passing in the DataFrame itself. Passing df.describe() directly gives a quick, instantly interactive statistics summary — tying back to the earlier pandas data cleaning article's use of .describe() as a first diagnostic step, now displayed directly within the dashboard itself.

Integrating Plotly
import plotly.express as px

fig = px.histogram(filtered_df, x="petal_length", color="species")
st.plotly_chart(fig, use_cTrue)

This integrates exactly the same way covered in the previous Plotly article — build the figure with px.histogram() (or any other Plotly Express or Graph Objects function) exactly as you would in a standalone script or Jupyter notebook — except fig.show() is replaced with st.plotly_chart(fig, use_cTrue), which renders the interactive Plotly chart directly within the Streamlit dashboard itself, rather than opening it separately in its own browser tab or notebook cell.

A practical combined example
col1, col2 = st.columns(2)

show_trend = st.checkbox("Show trend line", value=False)

with col1:
    fig1 = px.histogram(filtered_df, x="petal_length", color="species")
    st.plotly_chart(fig1, use_cTrue)

with col2:
    fig2 = px.scatter(filtered_df, x="petal_length", y="petal_length",
                        trendline="ols" if show_trend else None)
    st.plotly_chart(fig2, use_cTrue)

Placing two related Plotly charts side by side in st.columns(2), with a checkbox widget controlling one chart's appearance in real time, demonstrates the full rerun model from Section 2 concretely in action: toggling the checkbox reruns the entire script, show_trend picks up the new value, and fig2 is rebuilt with (or without) a trend line — all automatically, with no manual "update the chart" logic written anywhere.

Common Pitfalls and Scaling Beyond a Single Page

The most frequently hit beginner mistake
import streamlit as st

st.write("Hello!")   # any Streamlit command here...
st.set_page_config(page_title="My App")   # ...before this raises an error
StreamlitAPIException: set_page_config() can only be called once per app,
and must be called as the first Streamlit command in your script.

This is worth flagging explicitly, since it's genuinely the most common early mistake: st.set_page_config() must be the very first Streamlit command in the entire script — before st.write(), before referencing st.sidebar, before anything else Streamlit-related. Calling anything else first, even something as innocuous-looking as a single st.write(), raises a StreamlitAPIException immediately.

Best practices for polished dashboard visuals

Always set use_cTrue on charts, so they scale responsively to the available space, rather than rendering at a fixed size that might look cramped or oddly proportioned depending on the viewer's actual screen width. Keep a consistent color palette across multiple charts, using the same color_discrete_sequence parameter passed to each Plotly Express call — genuinely important for a dashboard's overall visual coherence, since inconsistent, randomly-assigned colors across different charts make it harder for a viewer to track a specific category visually as they move between panels. Add meaningful axis labels so charts are self-explanatory without needing a separate caption underneath explaining what they show.

Scaling to multiple pages

Streamlit's built-in multi-page architecture uses a genuinely simple convention: any .py file placed in a pages/ directory, sitting next to the entry-point script, automatically appears as a separate page in the sidebar navigation — no manual routing configuration, no separate page-registration step required.

my_dashboard/
├── main.py
└── pages/
    ├── 1_Overview.py
    └── 2_Detailed_Analysis.py

Streamlit automatically discovers both files inside pages/ and adds them as navigable pages, using the filename itself (minus the numeric prefix, which controls ordering) as the page's display name in the sidebar.

A closing note on when to graduate to Dash instead

Streamlit's rerun-everything model, covered throughout this article, is genuinely what makes it so fast to build with — but it's also precisely what becomes limiting once an application's needs grow more complex: genuinely fine-grained control over exactly which parts of a page update (rather than the entire script re-executing), more complex multi-user state management, or highly custom, non-standard layouts that Streamlit's straightforward vertical/columns model doesn't comfortably express. Dash (built on top of Plotly, exactly as covered in the previous Plotly article) offers considerably more granular control over callbacks and layout, at the cost of noticeably more code and a steeper learning curve than Streamlit's straightforward, script-rerun simplicity. For the overwhelming majority of internal data dashboards and quick, shareable analysis tools, Streamlit remains the faster, simpler starting point — reach for Dash specifically once you've genuinely outgrown what Streamlit's model comfortably supports, not preemptively.

PREVIOUSNEXT LESSON