Skip to content

Deploying ML Models to Streamlit

Not every audience wants a raw JSON API — a product manager or a stakeholder usually wants to type in some numbers and click a button. Streamlit lets you turn the same saved pipeline into a small interactive web page, using nothing but plain Python, so you can hand someone a link instead of a curlcurl command.

What Streamlit is

Streamlit turns Python scripts into web apps.

It’s great for:

  • demos
  • internal tools
  • quick validation with stakeholders

Where Streamlit fits in the deployment flow

diagram Diagram mermaid

Minimal Streamlit inference app

app.py (Streamlit)
# Requires: streamlit, joblib
import streamlit as st
import joblib
 
model = joblib.load("model.joblib")
 
st.title("ML Prediction Demo")
 
age = st.number_input("Age", min_value=0, max_value=120, value=30)
income = st.number_input("Income", min_value=0.0, value=50000.0)
city = st.text_input("City", value="Pune")
plan = st.selectbox("Plan", ["Free", "Pro", "Enterprise"])
 
if st.button("Predict"):
    X = [{"age": age, "income": income, "city": city, "plan": plan}]
    pred = model.predict(X)[0]
    st.write("Prediction:", pred)
app.py (Streamlit)
# Requires: streamlit, joblib
import streamlit as st
import joblib
 
model = joblib.load("model.joblib")
 
st.title("ML Prediction Demo")
 
age = st.number_input("Age", min_value=0, max_value=120, value=30)
income = st.number_input("Income", min_value=0.0, value=50000.0)
city = st.text_input("City", value="Pune")
plan = st.selectbox("Plan", ["Free", "Pro", "Enterprise"])
 
if st.button("Predict"):
    X = [{"age": age, "income": income, "city": city, "plan": plan}]
    pred = model.predict(X)[0]
    st.write("Prediction:", pred)

Running it locally

Run the Streamlit app
pip install streamlit joblib
streamlit run app.py
# -> opens http://localhost:8501 in your browser
Run the Streamlit app
pip install streamlit joblib
streamlit run app.py
# -> opens http://localhost:8501 in your browser

Don’t reload the model on every click

Streamlit re-runs your entire script from top to bottom every time a widget changes — every number you type, every button you click. Without caching, joblib.load("model.joblib")joblib.load("model.joblib") on line 4 would re-read the file from disk on every single interaction, which gets slow (or expensive) fast for a large model. @st.cache_resource@st.cache_resource tells Streamlit to run that function once and reuse the same object across reruns:

app.py (cached model load)
import streamlit as st
import joblib
 
@st.cache_resource
def load_model():
    return joblib.load("model.joblib")
 
model = load_model()  # only actually loads from disk the first time
app.py (cached model load)
import streamlit as st
import joblib
 
@st.cache_resource
def load_model():
    return joblib.load("model.joblib")
 
model = load_model()  # only actually loads from disk the first time

Use @st.cache_resource@st.cache_resource for things you don’t want copied (models, database connections). Use the related @st.cache_data@st.cache_data for cacheable data (like a CSV you read into a DataFrame) — Streamlit copies that result on each cache hit so callers can’t accidentally mutate the cached object.

Batch predictions from an uploaded file

A single-row form is fine for a demo, but stakeholders often want to drop in a whole spreadsheet and get predictions for every row back. st.file_uploaderst.file_uploader plus pandaspandas makes that a few lines:

app.py (batch predictions)
import streamlit as st
import pandas as pd
 
st.header("Batch predictions")
uploaded = st.file_uploader("Upload a CSV of rows to score", type="csv")
 
if uploaded is not None:
    df = pd.read_csv(uploaded)
    df["prediction"] = model.predict(df)
    st.dataframe(df)
    st.download_button(
        "Download results",
        df.to_csv(index=False),
        file_name="predictions.csv",
    )
app.py (batch predictions)
import streamlit as st
import pandas as pd
 
st.header("Batch predictions")
uploaded = st.file_uploader("Upload a CSV of rows to score", type="csv")
 
if uploaded is not None:
    df = pd.read_csv(uploaded)
    df["prediction"] = model.predict(df)
    st.dataframe(df)
    st.download_button(
        "Download results",
        df.to_csv(index=False),
        file_name="predictions.csv",
    )

Deployment options

  • Streamlit Community Cloud
  • Docker + any cloud VM
  • internal server

Keeping secrets out of the app

If your app needs an API key or database password, never hard-code it into app.pyapp.py. Streamlit reads a local .streamlit/secrets.toml.streamlit/secrets.toml file (and, on Streamlit Community Cloud, a “Secrets” panel in the app settings) and exposes it as st.secretsst.secrets:

.streamlit/secrets.toml
db_password = "correct-horse-battery-staple"
.streamlit/secrets.toml
db_password = "correct-horse-battery-staple"
Reading a secret
password = st.secrets["db_password"]
Reading a secret
password = st.secrets["db_password"]

Add .streamlit/secrets.toml.streamlit/secrets.toml to .gitignore.gitignore so it never ends up in version control.

Mini-checkpoint

When would you prefer Streamlit over an API?

  • when you need a user-facing demo UI quickly.

🧪 Try It Yourself

Exercise 1 – Build an Input Widget

Exercise 2 – Assemble the Feature Row for Prediction

Exercise 3 – Guard the Predict Button

Next

Continue to Dockerizing an ML Application — package the Streamlit app (or the Flask/FastAPI service) with its dependencies so it runs the same way on any machine.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did