Home › Guides › Streamlit Tutorial
Tutorial · 2026Streamlit Tutorial 2026: Build Your First Data App, Free-Tier Limits and When Not to Use It
This Streamlit tutorial shows you how to turn a Python script into a shareable web app: you write plain top-to-bottom Python, Streamlit reruns the whole file on every click, and caching plus session state keep it fast. Community Cloud hosts it free, capped at 2.7 GB of memory per app.
-
One idea explains Streamlit. Every widget interaction reruns your whole script from line 1, so
st.session_stateholds what must survive andst.cache_dataholds what you refuse to recompute. - The free tier is real but small. Streamlit's docs list 690 MB to 2.7 GB of memory per app on Community Cloud, and apps sleep after 12 hours without traffic.
- Pin your version. The docs warn that an unpinned app can be upgraded without your knowledge, which is the most common cause of "it worked last week".
- It scales further than critics admit. Snowflake's engineering blog describes more than 500 internal Streamlit apps covering financial reporting, security monitoring and product analytics.
- It is still the wrong tool for customer-facing products needing per-user data isolation, instant interaction and real auth. Internal tools and demos are its lane.
You have a working Python notebook. It loads a CSV, cleans three columns, groups by region and plots a chart your manager wants every Monday. So on Monday you run the notebook, screenshot the chart, and paste it into an email. Again. Streamlit exists to end that loop, and about twenty lines of Python is all it takes, which is why it has become the default way Indian data and AI teams put a first interface over their work.
What Streamlit Is, and the Rerun Model Behind Every Streamlit Tutorial
Streamlit is an open-source Python library that renders a web app from a normal Python script. No route table, no template language, no JavaScript build step. You write st.selectbox("Region", regions) and a dropdown appears, and the function returns whatever the user picked. The latest release is 1.66.0, dated 14 October 2026 in Streamlit's own release notes.
The part most tutorials bury on page four is the execution model. Streamlit does not bind an event handler to that dropdown. When the user changes it, Streamlit reruns your entire script from the first line to the last, with the dropdown now returning the new value. Your app is not a long-lived object holding state. It is a function that gets called again, completely, on every interaction. Accept that and the two APIs below stop looking like workarounds: anything you want to keep has to live somewhere Streamlit does not throw away, and anything expensive has to be cached or you pay for it on every click.
The Streamlit rerun model
A click does not fire a callback. It re-executes your whole file, so only cache and session state carry anything forward.
Behaviour described in Streamlit's caching and session state documentation, checked 28 October 2026.
Streamlit Tutorial: Build Your First Data App in 20 Lines
Install it and run the bundled demo first. If streamlit hello opens in your browser, your environment is fine and every later problem is your code, not your setup.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install streamlit pandas
streamlit hello # opens the demo app on localhost:8501
Now the real thing. Save this as app.py. It takes a CSV upload, lets the user filter it, and draws a chart, which covers most of what internal data apps actually do.
import pandas as pd
import streamlit as st
st.set_page_config(page_title="Regional sales", layout="wide")
st.title("Regional sales explorer")
uploaded = st.file_uploader("Upload a sales CSV", type="csv")
if uploaded is None:
st.info("Waiting for a file. It needs region, month and revenue columns.")
st.stop()
df = pd.read_csv(uploaded)
regions = sorted(df["region"].unique())
picked = st.multiselect("Regions", regions, default=regions)
floor = st.slider("Minimum revenue", 0, int(df["revenue"].max()), 0)
view = df[df["region"].isin(picked) & (df["revenue"] >= floor)]
st.metric("Rows shown", len(view), delta=len(view) - len(df))
st.bar_chart(view.groupby("month")["revenue"].sum())
st.dataframe(view, use_container_width=True)
Run it with streamlit run app.py. Two details are worth stealing. st.stop() halts the run cleanly instead of letting the rest of the page crash on a missing file, which is the idiomatic way to say "the user has not given me enough yet". And st.metric with a delta gives you the green or red arrow free. Notice what is absent: no if __name__ == "__main__", no server setup, no HTML. The script is the app.
Keep a two-person analytics team at a mid-size logistics firm in Pune in mind for the rest of this. One Postgres replica, a Monday reporting ritual, no front-end developer. That team is Streamlit's centre of gravity, and most of the calls below are theirs.
Streamlit Session State and Caching: The Two APIs That Decide Whether Your App Feels Fast
Here is the bug every beginner writes. Add a counter to the app above and it refuses to count.
# BROKEN: resets on every click, because the whole script reruns
count = 0
if st.button("Add one"):
count += 1
st.write(count) # prints 0 or 1, never 2
# WORKING: streamlit session state survives the rerun
if "count" not in st.session_state:
st.session_state.count = 0
if st.button("Add one"):
st.session_state.count += 1
st.write(st.session_state.count)
Streamlit session state is a dict scoped to one browser session. It survives reruns, not a page refresh, and it is not shared between users. Use it for anything with a sequence: a chat history, a multi-step form, a pointer to the selected row.
Streamlit cache_data, and the mistake that makes caching pointless
@st.cache_data memoises a return value keyed on the arguments, shared across every user of the app. @st.cache_resource is its sibling for things you want exactly one of, such as a connection or a loaded model.
@st.cache_data(ttl=600, max_entries=50, show_spinner="Querying Postgres...")
def load_sales(region: str, month: str) -> pd.DataFrame:
# Runs once per (region, month), then served from memory for 600s
return pd.read_sql(
"SELECT * FROM sales WHERE region = %s AND month = %s",
get_connection(), params=(region, month),
)
@st.cache_resource
def get_connection():
return psycopg2.connect(**st.secrets["postgres"]) # one per app, not per rerun
The mistake is caching a function whose arguments change on every rerun, which buys you a 0% hit rate and a growing memory footprint. Pass a DataFrame in and Streamlit has to hash it to build the key; on a wide frame that hashing can cost more than the work you were skipping. Pass the small things, the region code and the date range, and let the cached function fetch the big thing itself.
The opinionated bit: learn cache_data second, right after your first chart, not last. Most tutorials treat it as an optimisation chapter. On a free-tier host it is the difference between an app your manager opens twice and one they bookmark.
| Call | What it does | The gotcha nobody mentions |
|---|---|---|
st.dataframe(df) |
Interactive sortable table |
st.table is the static one. Sending 500k rows to the browser hangs the tab. |
st.selectbox / multiselect |
Returns the chosen value | The value, not an index. Changing it triggers a full rerun. |
st.file_uploader |
Returns a file-like object | There is a per-file cap; raise server.maxUploadSize before users meet it. |
st.session_state |
Per-session dict that survives reruns | Cleared by a browser refresh. It is not a database. |
@st.cache_data |
Memoises a return value | Shared across users. Never cache a per-user result without the user in the key. |
st.form |
Batches widgets into one submit | The cure for apps that rerun on every keystroke. |
st.secrets |
Reads secrets like a dict | A missing key raises at render time, in front of the user. |
@st.fragment |
Reruns one part of the page | Release 1.63.0 added event-scoped fragment reruns keyed on the decorator. Reach for it when the app is slow, not before. |
API behaviour and the 1.63.0 note taken from Streamlit's API reference and 2026 release notes, checked 28 October 2026.
How to Deploy a Streamlit App Free on Community Cloud
The deploy story is the best thing about Streamlit: three files and about four minutes. Push to GitHub, point Community Cloud at the repo, branch and entrypoint, and it builds the container for you.
# requirements.txt : pin everything, including streamlit itself
streamlit==1.66.0
pandas==2.2.3
psycopg2-binary==2.9.9
# .streamlit/secrets.toml : local only, add to .gitignore
[postgres]
host = "db.internal"
user = "readonly"
password = "rotate-me-after-deploy"
Three things from Streamlit's deployment docs that will save you an afternoon. Do not list standard library modules such as math or random in requirements.txt; the docs call that out and it is a common cause of a failed build. Streamlit is preinstalled, so you list it only to pin the version, which the docs recommend because an unpinned app can be upgraded without your knowledge. And never commit secrets.toml: paste its contents into the Advanced settings box at deploy time and edit them from the app's settings later.
If you have worked through our Power BI tutorial for beginners, the contrast is instructive. Power BI gives you governed, licensed distribution; Streamlit gives you a URL in four minutes. Both answer different questions, and the Data Analyst course builds the licensed route live through PL-300.
Community Cloud free tier, in three numbers
Enough for a portfolio app or an internal tool. Not enough to load a large model into memory.
From Streamlit's Community Cloud documentation and 2026 release notes, checked 28 October 2026. The docs state these limits may change at any time without notice.
Streamlit Community Cloud Limits: Where Apps Die, and the Arithmetic Behind It
Streamlit's docs say every Community Cloud user gets the same resources and the same limits, and that apps crossing them may be throttled or stop working. That stays abstract until you convert it into rows. A pandas DataFrame of 64-bit floats costs 8 bytes per cell, so ten numeric columns is 80 bytes per row. That is arithmetic, not a benchmark, and df.memory_usage(deep=True).sum() will confirm it on your own data.
How many rows fit in 2.7 GB
Ten float64 columns at 8 bytes per cell. String columns are far worse, often 5 to 10 times this.
Row counts are arithmetic from 8 bytes per float64 cell against the 2.7 GB ceiling in Streamlit's docs, checked 28 October 2026.
Raw size is rarely what kills a free-tier app. Copies are. The cached loader holds the full frame, your filter makes a second, a groupby makes a third, and cache_data keeps one entry per argument combination anyone has tried; a 400 MB table becomes 2 GB of cache by Thursday. Set a ttl and max_entries, and push filtering into SQL. Also read: our DuckDB tutorial, which pairs with Streamlit perfectly here, because DuckDB aggregates in C++ and hands back a hundred rows to render.
For more headroom, Hugging Face's Hub docs list CPU Basic at 2 vCPU, 16 GB memory and 50 GB disk at no hourly cost, with CPU Upgrade at 8 vCPU and 32 GB for 0.03 US dollars per hour, though their docs note a compute Space can require a paid plan. Past that you provision your own container and own TLS, auth and uptime, which is where a cloud and DevOps skill set pays for itself.
Where Streamlit Shows Up in Real Work, and the 500-App Case Study
The most useful public example is Snowflake's engineering blog, which describes an internal estate of more than 500 Streamlit apps. The part worth reading twice is how it began: a handful of experimental dashboards from one team, which grew into a platform carrying financial reporting, security monitoring and product analytics. Nobody approved a Streamlit strategy. Analysts shipped apps because shipping one took an afternoon. Expect the same pattern in a job, in roughly this order of frequency:
The internal tool that replaced a spreadsheet. Our Pune team's Monday ritual becomes a URL with a date picker, and finance stops asking for the file. Least glamorous, highest value, fastest way for an analyst to get noticed.
The demo that sells the model. An AI engineer with a working retrieval pipeline needs a chat box in front of it for the stakeholder review, and st.chat_message plus st.chat_input gets there in thirty lines. Building that retrieval layer properly is what the AI Engineer course practises live on the Claude and Microsoft stacks. For the retrieval side first, read how to build production RAG.
The labelling and review harness. Someone has to read 400 model outputs and mark each good or bad. A session-state pointer, two buttons and a CSV write is a two-hour build, and it is standard MLOps plumbing that teams reinvent constantly.
A Streamlit app is the front door. Learn to build what sits behind it.
The AI Engineer course teaches you to build agents that plan, use tools and act, certified on both Microsoft Copilot Studio and Claude Code, the two stacks real job postings are naming. Retrieval, tool calling and agent design are built live across 16 weeks.
Explore the course
Four Things That Go Wrong in Every First Streamlit App
Here is the one that will actually bite you, warned the way a colleague would rather than the way docs do. You will put an API key in secrets.toml, add the file to .gitignore, feel responsible, and then two weeks later commit a notebook whose output cell contains the key. The leak almost never comes from the file you remembered to ignore. Rotate the key once you deploy, and never hand a Streamlit app a write-capable database credential. Read-only user, always.
Four failures and what to do instead
Each one has the same root cause: forgetting that the script reruns.
Reruns on every keystroke
A st.text_input next to an expensive query means a query per character typed. Wrap the inputs in st.form so nothing fires until submit.
Cache leaking between users
cache_data is shared across everyone using the app. Cache a per-user query without the user in the key and person A sees person B's rows.
Unpinned Streamlit version
The docs warn an unpinned app can be upgraded without your knowledge. One morning it is broken and your repo has not changed.
Pin itLoading the whole table
SELECT * FROM events is the fastest route to the memory ceiling. Aggregate in the database, render the summary, keep the detail one click away.
Compiled from Streamlit's caching, session state and deployment docs, checked 28 October 2026.
| Symptom | What is actually happening | Fix |
|---|---|---|
| "Over its resource limits", app goes blank | Memory crossed the ceiling, usually from cached copies rather than one big frame | Add ttl and max_entries, aggregate in SQL, cast float64 to float32 where precision allows |
| Button needs two clicks to work | You read session state before the rerun that writes it | Initialise the key at the top of the script, or use the widget's on_change callback |
| Chart redraws, numbers are stale | Cache hit on arguments that did not change, even though the table did | Set a ttl, or expose an admin button calling st.cache_data.clear()
|
| Works locally, ModuleNotFoundError on deploy | The dependency was in your virtualenv but not in requirements.txt
|
Regenerate from a clean environment, and leave standard library modules out |
KeyError on st.secrets in production only |
Secrets were never pasted into Advanced settings at deploy time | Add them in the app's settings, then reboot the app |
When Not to Use Streamlit
Every tool deep dive owes you this section, so here is the honest version. Streamlit is a poor fit the moment any of these is true, and no amount of clever engineering changes that cheaply.
- Different users must see different data. There is no built-in authorisation model. Streamlit documents tutorials for Google and Microsoft Entra authentication, but data isolation stays your problem and the shared cache works against you.
- The interaction must feel instant. A full script rerun per click has a floor. Fragments help, but a drag-and-drop canvas or a smooth animated map wants a real front end.
- It is a product, not a tool. Custom routing, SEO, mobile layouts and design systems all fight the framework. A competitor benchmark circulating in 2026 claims 15.6 seconds to first data for Streamlit on simulated slow 4G against 2.1 seconds for their own framework; it is vendor marketing measuring first paint rather than interactivity, so distrust the figures, but not the direction.
- Many concurrent users on small hardware. Each session holds its own state in the same process. Fifty analysts at 9 am on one small container is a bad morning.
There is a quieter trap too. Streamlit is pleasant enough that teams build the app instead of fixing the pipeline, and a prettier interface onto a wrong number makes things worse. Fix the model layer first: a governed Microsoft Fabric lakehouse is the right home for that logic, and the DP-700 syllabus in that programme is largely about this discipline.
What to Build Next, in Order
With an hour a day, this beats working through the API reference. Build the twenty-line app above with your own CSV tonight. Add session state and a form tomorrow. Add cache_data with a ttl on day three, deploy on day four so the thing has a URL you can send someone. Only in week two go near fragments, custom components or multi-page apps.
So, what would I do in your position? Build it tonight with a messy CSV you already own, deploy it tomorrow, and send the link to one person who will actually use it. Streamlit's value is not sophistication, because it has none; it is that the gap between a notebook and a URL someone else can open shrinks to an evening, and little else in a data career compounds faster than being the person who closes that gap. If what you want behind that URL is a retrieval or agent system, the AI Engineer course is where to build one properly, a free webinar is the cheapest way to see the syllabus first, and the full certifications overview maps which exams sit on which route.
Related guides
- Data Analyst Portfolio Projects in 2026 six builds that each deserve a Streamlit front end once the SQL works.
- Certification vs Projects in 2026 decide whether the deployed app or the exam badge goes first on your CV.
- AI Engineer Salary in India 2026 what the role pays by experience and city, and which skills move the band.
- AI Engineer Interview Questions 2026 the questions a deployed demo app immediately invites.
- What Is Agentic RAG? the retrieval loop worth putting behind that chat box, with real token costs.
- Data Analyst Roadmap 2026 where Python app building fits in a seven-step route to a first analytics job.
Frequently asked questions
Is this Streamlit tutorial enough to build a real app, or do I need web development?
No HTML, CSS or JavaScript is required. The twenty-line example above is a complete app with upload, filtering and a chart. Web skills only matter once you want custom layouts or components, which is also the point to ask whether Streamlit is still the right tool.
Is Streamlit free, and what are the Streamlit Community Cloud limits?
The library is open source and free, and Community Cloud hosting is free too. Streamlit's docs list memory per app as 690 MB minimum to 2.7 GB maximum, with apps put to sleep after 12 hours without traffic, and state that these limits can change without notice.
What is the difference between st.session_state and st.cache_data?
Session state is per browser session and remembers values such as a chat history or the selected row. Cache data is shared across all users and stores results you do not want to recompute, keyed on the arguments. Caching per-user data is a security bug, not a style choice.
Why does my Streamlit app reset the value every time I click a button?
Because the whole script reruns from line 1 on every interaction, so count = 0 executes again and the increment is lost. Move the value into st.session_state, guard the initialisation with an if "count" not in st.session_state check, and it survives.
How do I deploy a Streamlit app for free?
Push the app to a GitHub repo with a requirements.txt, sign in to Community Cloud, pick the repo, branch and entrypoint file, then paste your local .streamlit/secrets.toml contents into Advanced settings. Pin the Streamlit version in requirements, as the docs recommend.
Can Streamlit handle large datasets?
The constraint is your host's memory, not the library. At 8 bytes per float64 cell, ten numeric columns means roughly 34 million rows fills the documented 2.7 GB ceiling before cached and filtered copies. Aggregate in SQL or DuckDB and render only the summary.
Is Streamlit good enough for production, or only for prototypes?
It runs in production in plenty of places. Snowflake's engineering blog describes more than 500 internal Streamlit apps covering financial reporting, security monitoring and product analytics. It is production-grade for internal tools and weak for customer-facing products needing per-user authorisation and instant interaction.
About this guide. 360 Digital Transformation is an Anthropic Partner and a Microsoft Authorized Training Partner. Other certification bodies, vendors and employers named here are not affiliated with us. Tools and versions change quickly; commands and figures cited were checked on 28 October 2026.




