Module 1 — First steps and the Streamlit execution model
Streamlit lets a Python author turn a script into a web application without touching HTML, JavaScript or any front-end framework. The promise is real, but it rests on one design decision that surprises every newcomer: the entire script is re-executed from top to bottom every time the user interacts with the page. Understanding that single fact prevents about 80 % of the questions asked on the community forum, and it shapes every module of this course.
Installation and the first run
Streamlit is a plain PyPI package. A virtual environment is strongly
recommended: the framework pins recent versions of pandas, numpy,
altair and pyarrow, and mixing those with an older global environment
is a recipe for import-time surprises.
# terminal
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install streamlit==1.36.0 pandas scikit-learn joblib
streamlit --version
A first application takes seven lines:
# app.py
import streamlit as st
st.title("Churn dashboard")
st.write("Hello, sales team.")
name = st.text_input("Customer name")
if name:
st.success(f"You typed {name}.")
Launching is a single command, and it opens a browser tab on
http://localhost:8501:
# terminal
streamlit run app.py
That command starts a small Tornado server, which serves the HTML shell once, then talks to the browser through a websocket. Every widget your script creates has an identity on that channel, and every user gesture sends a message back to the server that triggers the piece of behavior described in the next section.
The rerun-on-interaction execution model
Type a letter in the text_input widget and Streamlit does not call a
callback attached to that widget. It re-executes app.py from the top
to the bottom, with name now equal to the current text. st.title
runs again, st.write runs again, st.text_input runs again — and its
return value is your new letter. The old page is diffed against the new
one, only what changed is sent over the wire.
This model has three consequences that beginners repeatedly stumble on.
First, top-level code is not a one-time setup. If you write
counter = 0 at the top of the file and then increment it on a button
click, counter will be reset to zero on the very next interaction. State
that must survive reruns belongs in st.session_state, covered in
module 6.
Second, expensive work runs on every interaction unless you cache it.
A model loaded with joblib.load("model.pkl") on line 3 will be reloaded
from disk every time the user moves a slider. On a 200 MB model that is
seconds of lag per keystroke, and it is one of the most common performance
bugs of new Streamlit apps. Module 5 introduces @st.cache_data and
@st.cache_resource for exactly that reason.
Third, the order of widgets defines their identity. Streamlit
identifies a widget by its position and its label; renaming a label or
inserting a widget above it resets the widget's state. Passing a stable
key="customer_name" argument decouples identity from position and is a
habit worth adopting early.
What a rerun does not do
A rerun re-executes Python, not the browser tab. The HTML shell, the CSS theme, the websocket and the browser's scroll position stay in place. Two things you might expect to happen do not:
- Random state is fresh. Calling
random.random()at the top of your script gives a new value on every rerun. If you want a stable random choice, seed it (random.seed(42)) or store the drawn value inst.session_state. - Print statements go to the terminal, not to the page. Streamlit
writes them to the process's standard output, which is invisible in the
browser. Use
st.write,st.dataframeor the sidebar for on-page logging.
To force a rerun from code, call st.rerun(). To stop the current run
early — for example after a validation error — call st.stop(). Both are
useful and both live in the standard library of Streamlit gestures.
The first page of the running example
Enough theory. Here is the first version of the churn dashboard the sales team will use. It has one widget, no cache and no state yet, but it already shows the top-to-bottom flow at work.
# app.py
import streamlit as st
import pandas as pd
st.set_page_config(page_title="Churn dashboard", page_icon="📉", layout="wide")
st.title("Churn dashboard")
st.caption("Sales team — internal use only")
st.header("Score one customer")
tenure = st.slider("Months as a customer", min_value=0, max_value=72, value=12)
monthly_charge = st.number_input("Monthly bill (USD)", min_value=0.0, value=70.0, step=1.0)
contract = st.selectbox("Contract type", ["Month-to-month", "One year", "Two years"])
if st.button("Score this customer"):
# A placeholder rule that returns a probability between 0 and 1.
base = 0.5 - 0.005 * tenure + 0.002 * monthly_charge
penalty = {"Month-to-month": 0.15, "One year": 0.0, "Two years": -0.10}[contract]
probability = max(0.0, min(1.0, base + penalty))
st.metric("Estimated churn probability", f"{probability:.0%}")
Two conventions in this snippet are worth pointing out. st.set_page_config
must be the first Streamlit call in the script; putting it anywhere
else raises a StreamlitAPIException. And the if st.button(...) block
only enters the branch on the rerun triggered by the click itself: on the
very next rerun caused by, say, moving the slider, the button returns
False again and the metric disappears. Persisting the result across
reruns is the job of st.session_state in module 6.
The little menu in the top right corner of every Streamlit page exposes
"Rerun", "Clear cache" and "Settings". "Clear cache" is the fastest way to
recover from a stale @st.cache_data result during development, and
knowing it exists saves a lot of confused restarts.
In summary
- Streamlit re-runs your script top to bottom on every interaction; design against that fact, do not fight it.
- Top-level variables are not persistent; state that must survive belongs
in
st.session_state(module 6) and expensive work belongs behind a cache (module 5). st.set_page_configmust be the first Streamlit call; widget identity is derived from label and position, so pass a stablekey=as soon as your layout might change.streamlit run app.pystarts a small server and a websocket; print statements go to the terminal, onlyst.*calls appear in the browser.
Next module: input and display components, the vocabulary from which the scoring form of this course is built.