Flask gives you total control but requires you to write HTML. Streamlit inverts that: you write plain Python, and Streamlit turns your script into a live web app with buttons, sliders, and charts automatically. It is the fastest way to let people interact with your models and data.
Install Streamlit, then run any script with streamlit run. Every time a widget changes, Streamlit re-runs the whole script from top to bottom — so your app is just a normal Python script.
pip install streamlit
st.write is the catch-all that displays text, data, or charts. Widgets like buttons, sliders, text inputs, and select boxes store the user's choice in a variable.
import streamlit as st
st.title("My First App")
name = st.text_input("What is your name?")
age = st.slider("Select your age", 0, 100, 25)
likes = st.radio("Favorite color", ["Red", "Blue", "Green"])
if st.button("Say hello"):
st.write(f"Hello {name}, age {age}, you chose {likes}.")
Columns arrange content side by side, and st.sidebar moves widgets into a panel that stays visible as users scroll.
import streamlit as st
import pandas as pd
df = pd.DataFrame({"value": [3, 7, 2, 9, 4]})
with st.sidebar:
bins = st.slider("Number of bins", 1, 20, 5)
color = st.color_picker("Bar color", "#7c3aed")
col1, col2 = st.columns(2)
with col1:
st.metric("Mean", round(df["value"].mean(), 2))
with col2:
st.metric("Max", df["value"].max())
st.bar_chart(df, color=color)
st.write(f"Chart drawn with {bins} bins of color {color}.")
@st.cache_data so it only happens once. The decorator remembers the result for the given inputs, which keeps the app fast as users tweak sliders.
import streamlit as st
import time
@st.cache_data
def load_big_file(path):
time.sleep(2) # simulate a slow read
return [line.strip() for line in open(path)]
lines = load_big_file("words.txt")
st.write(f"Loaded {len(lines)} lines. First: {lines[0]}")
The classic pattern: load a trained model, collect inputs with widgets, make a prediction, and display the result. This example uses a simple linear regression from scikit-learn that we train on the fly so the app runs anywhere.
import streamlit as st
import numpy as np
from sklearn.linear_model import LinearRegression
@st.cache_resource
def train_model():
rng = np.random.default_rng(7)
x = rng.uniform(0, 10, 200).reshape(-1, 1)
y = 3 * x.ravel() + rng.normal(0, 1, 200)
model = LinearRegression()
model.fit(x, y)
return model
st.title("House Price Estimator (demo)")
st.write("Enter the key features and get a prediction.")
size = st.number_input("Size (square meters)", min_value=20.0, max_value=400.0, value=80.0)
rooms = st.slider("Number of rooms", 1, 6, 3)
year = st.slider("Year built", 1950, 2025, 2005)
features = np.array([size + rooms, rooms, 2025 - year]).reshape(1, -1)
if st.button("Predict price"):
model = train_model()
# demo model actually uses only one feature; we show the pattern
prediction = 1200 * size + 900 * rooms - 500 * (2025 - year)
st.success(f"Estimated price: ${prediction:,.0f}")
with st.expander("How it works"):
st.markdown("In a real project, `model.predict(features)` replaces the "
"formula above. The model was trained on 200 synthetic "
"samples and is cached with `@st.cache_resource`.")
@st.cache_resource keeps heavyweight objects (models, database connections) alive across reruns, while @st.cache_data caches plain data like DataFrames. Reserve a st.expander or st.markdown for explanations rather than crowding the UI.
app.py and add a requirements.txt listing every import you use.share.streamlit.io) with GitHub, click "New app", pick the repo, branch, and file, and press Deploy.requirements.txt and gives you a public URL like https://yourname-project-name.streamlit.app.# requirements.txt
streamlit
numpy
pandas
scikit-learn
st.secrets to read them from the platform's secrets manager, and set PYTHONUNBUFFERED=1 in the cloud settings so logs stream properly.
Build an interactive data-explorer app:
st.file_uploader.st.dataframe and basic stats with df.describe().st.scatter_chart.st.metric.import streamlit as st
import pandas as pd
st.title("CSV Explorer")
uploaded = st.file_uploader("Choose a CSV file", type="csv")
if uploaded is not None:
df = pd.read_csv(uploaded)
st.dataframe(df.head(100))
st.write(df.describe())
numeric = df.select_dtypes(include="number").columns.tolist()
if numeric:
x = st.selectbox("X axis", numeric)
y = st.selectbox("Y axis", numeric, index=min(1, len(numeric) - 1))
st.scatter_chart(df[[x, y]])
st.metric(f"Correlation ({x} vs {y})",
round(df[x].corr(df[y]), 3))
else:
st.info("No numeric columns found in this file.")