Streamlit is the fastest way to turn a machine learning model or data analysis script into a shareable interactive web application — requiring no frontend development experience. A data scientist who would spend weeks building a Flask + React dashboard can build the same interactive app in Streamlit in a few hours. This has made Streamlit the standard tool for ML demos, internal analytics tools, model monitoring dashboards, and rapid prototyping. This guide covers the complete Streamlit development workflow — from a basic app to a production-ready ML deployment with authentication, caching, and model serving.
Streamlit deployment is the final step in the ML workflow that starts with feature engineering (our Feature Engineering guide), model training and evaluation (our Model Evaluation guide), and production deployment considerations (our MLOps Interview Q&A). The visualisations embedded in Streamlit apps use the libraries in our Data Visualization guide. The models deployed via Streamlit are trained using techniques from our Machine Learning Interview Q&A and Gradient Boosting guide.
Streamlit Fundamentals — How It Works
Streamlit’s execution model is simple: every time the user interacts with a widget (slider, button, text input), the entire Python script re-runs from top to bottom, and Streamlit re-renders only the changed elements. This reactive model means you write linear Python scripts rather than callback functions — dramatically lowering the learning curve. The tradeoff: computationally expensive operations (loading data, training models, calling APIs) must be cached to avoid re-running on every interaction.
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
import joblib
# --- Page configuration (must be first Streamlit call) ---
st.set_page_config(
page_title='ML Model Dashboard',
page_icon='chart_with_upwards_trend',
layout='wide',
initial_sidebar_state='expanded'
)
# --- Caching: st.cache_data for data, st.cache_resource for models ---
@st.cache_data
def load_data(file_path):
'''Load and cache dataset — runs only when file_path changes.'''
df = pd.read_csv(file_path)
df['date'] = pd.to_datetime(df['date'])
return df
@st.cache_resource
def load_model(model_path):
'''Load and cache model object — shared across sessions.'''
return joblib.load(model_path)
# --- Sidebar: user inputs ---
st.sidebar.title('Model Configuration')
model_type = st.sidebar.selectbox('Model', ['GBM', 'RandomForest', 'LogisticRegression'])
threshold = st.sidebar.slider('Decision Threshold', 0.0, 1.0, 0.5, 0.01)
date_range = st.sidebar.date_input('Date Range', value=(pd.Timestamp('2026-01-01'),
pd.Timestamp('2026-09-01')))
show_raw = st.sidebar.checkbox('Show Raw Data', value=False)
# --- Main page ---
st.title('Churn Prediction Dashboard')
st.markdown('Real-time model scoring and performance monitoring.')
col1, col2, col3, col4 = st.columns(4)
col1.metric('Total Customers', '124,532', delta='+1,240')
col2.metric('At-Risk Customers', '8,721', delta='+312', delta_color='inverse')
col3.metric('Model AUC', '0.847', delta='+0.003')
col4.metric('Intervention Rate', '7.0%', delta='-0.2%', delta_color='inverse')
Building an End-to-End ML App
# --- File uploader + prediction ---
st.subheader('Batch Scoring')
uploaded = st.file_uploader('Upload customer CSV', type=['csv'])
if uploaded is not None:
df_input = pd.read_csv(uploaded)
st.write('Uploaded:', df_input.shape[0], 'rows,', df_input.shape[1], 'columns')
if st.button('Run Predictions', type='primary'):
with st.spinner('Scoring...'):
model = load_model('churn_model.pkl')
probs = model.predict_proba(df_input)[:, 1]
preds = (probs >= threshold).astype(int)
df_out = df_input.copy()
df_out['churn_prob'] = probs.round(4)
df_out['churn_pred'] = preds
df_out['risk_tier'] = pd.cut(probs,
bins=[0, 0.3, 0.6, 1.0],
labels=['Low', 'Medium', 'High'])
# Metrics
c1, c2, c3 = st.columns(3)
c1.metric('Predicted Churners', int(preds.sum()))
c2.metric('Avg Churn Probability', str(round(probs.mean()*100, 1)) + '%')
c3.metric('High Risk Customers', int((probs >= 0.6).sum()))
# Distribution plot
fig = px.histogram(df_out, x='churn_prob', nbins=40, color='risk_tier',
title='Predicted Churn Probability Distribution',
color_discrete_map={'Low':'green','Medium':'orange','High':'red'})
st.plotly_chart(fig, use_container_width=True)
# Download results
csv_out = df_out.to_csv(index=False).encode('utf-8')
st.download_button('Download Results CSV', csv_out,
'churn_predictions.csv', 'text/csv')
if show_raw:
st.dataframe(df_out, use_container_width=True)
# --- Real-time single prediction form ---
st.subheader('Single Customer Scoring')
with st.form('customer_form'):
tenure = st.number_input('Tenure (months)', 0, 120, 24)
spend = st.number_input('Monthly Spend (INR)', 0, 50000, 1200)
n_calls = st.number_input('Support Calls (last 3 months)', 0, 50, 2)
plan = st.selectbox('Plan Type', ['Basic', 'Standard', 'Premium'])
submitted = st.form_submit_button('Predict Churn Risk')
if submitted:
plan_enc = {'Basic': 0, 'Standard': 1, 'Premium': 2}[plan]
row = np.array([[tenure, spend, n_calls, plan_enc]])
model = load_model('churn_model.pkl')
prob = model.predict_proba(row)[0, 1]
col_a, col_b = st.columns(2)
col_a.metric('Churn Probability', str(round(prob*100, 1)) + '%')
risk = 'High' if prob >= 0.6 else ('Medium' if prob >= 0.3 else 'Low')
color = 'red' if risk == 'High' else ('orange' if risk == 'Medium' else 'green')
col_b.markdown('Risk Tier: **:' + color + '[' + risk + ']**')
if prob >= 0.6:
st.warning('This customer is at high churn risk. Consider a retention offer.')
Performance, Caching and Production Patterns
Session state — persisting data across reruns: Streamlit’s re-run model resets all local variables on each interaction. Use st.session_state to persist data across reruns within a session:
# Persist state across reruns
if 'predictions' not in st.session_state:
st.session_state.predictions = None
if 'model_loaded' not in st.session_state:
st.session_state.model_loaded = False
if st.button('Load Model'):
st.session_state.model_loaded = True
st.session_state.model = load_model('model.pkl')
if st.session_state.model_loaded:
st.success('Model ready')
Multi-page apps: Organise large apps into pages — create a pages/ directory with separate .py files. Streamlit automatically adds navigation. Structure: main_app.py (entry point), pages/1_Overview.py, pages/2_Predictions.py, pages/3_Monitoring.py.
Deployment options:
| Platform | Best For | Cost | Setup Effort |
|---|---|---|---|
| Streamlit Community Cloud | Public demos, open-source apps | Free | Minimal — connect GitHub repo |
| AWS EC2 + nginx | Production internal tools | ~$10-50/month | Medium — server setup required |
| GCP Cloud Run (containerised) | Scalable, auto-scaling | Pay per request | Medium — Docker + Cloud Run |
| Azure Container Instances | Enterprise, Azure ecosystem | Pay per use | Medium |
| Hugging Face Spaces | ML demos, Gradio/Streamlit apps | Free tier available | Minimal — connect GitHub |
For deploying models at production scale with versioning, monitoring, and CI/CD — beyond what Streamlit handles — our MLOps Interview Q&A covers FastAPI model serving, Docker containerisation, MLflow model registry, and data drift monitoring. For the data pipelines that feed Streamlit dashboards, our Data Engineering Fundamentals guide covers Airflow, dbt, and the modern data stack. The Plotly visualisations embedded in Streamlit apps are covered in depth in our Data Visualization guide.



