Streamlit Study Guide
Build and deploy data apps in pure Python β no frontend skills needed.
10 Topics • Interactive Web AppsInstall Streamlit and build your first interactive web app in minutes.
Hello World App
# Install: pip install streamlit
# Run: streamlit run app.py
import streamlit as st
st.set_page_config(page_title='My App', page_icon='π', layout='wide')
st.title('Hello, Streamlit!')
st.subheader('Your first data app')
st.write('Streamlit turns Python scripts into shareable web apps.')
name = st.text_input('What is your name?', 'World')
st.success(f'Hello, {name}! Welcome to Streamlit.')
if st.button('Celebrate!'):
st.balloons()Page Config & Theming
import streamlit as st
# Page config must be first Streamlit call
st.set_page_config(
page_title='Data Dashboard',
page_icon='π',
layout='wide',
initial_sidebar_state='expanded'
)
# Display info boxes
st.info('This is an info message')
st.warning('This is a warning')
st.error('This is an error')
st.success('This is a success message')
# Code block
st.code('import pandas as pd\ndf = pd.read_csv("data.csv")', language='python')
# Metric cards
col1, col2, col3 = st.columns(3)
col1.metric('Revenue', '$45,230', '+12%')
col2.metric('Users', '1,234', '+5%')
col3.metric('Churn', '2.4%', '-0.3%')Custom Theme, Favicon & Wide Layout
# Run: streamlit run app.py
import streamlit as st
# st.set_page_config MUST be the very first Streamlit call
# page_icon can be an emoji string or a path to a .png file
st.set_page_config(
page_title='Custom Theme Demo',
page_icon='π¨', # appears in browser tab
layout='wide', # full-width layout
initial_sidebar_state='collapsed'
)
# Override theme at runtime via CSS injection
st.markdown(
'<style>h1{{color:#ff4b4b;}} .stMetric{{background:#1e1e2e;border-radius:8px;padding:12px;}}</style>',
unsafe_allow_html=True
)
st.title('Custom Theme Demo')
st.caption('page_icon, layout=wide, and injected CSS all set at startup')
c1, c2, c3, c4 = st.columns(4)
c1.metric('CPU', '42%', '-3%')
c2.metric('RAM', '6.2 GB', '+0.4')
c3.metric('Latency', '18 ms', '-2 ms')
c4.metric('Errors', '0', '0')
# You can also store theme colours in a .streamlit/config.toml:
# [theme]
# primaryColor = '#ff4b4b'
# backgroundColor = '#0d1117'
# secondaryBackgroundColor = '#161b22'
# textColor = '#e6edf3'st.balloons, st.snow & sidebar_state Options
# Run: streamlit run app.py
import streamlit as st
st.set_page_config(
page_title='Celebrations Demo',
page_icon='π',
layout='centered',
# sidebar_state options: 'auto', 'expanded', 'collapsed'
initial_sidebar_state='expanded'
)
st.title('Celebrations & Sidebar State')
with st.sidebar:
st.header('Sidebar Controls')
st.write('initial_sidebar_state="expanded" opens this on load.')
name = st.text_input('Your name', 'World')
st.write(f'Hello, **{name}**!')
col1, col2 = st.columns(2)
# st.balloons β rising colourful balloons animation
if col1.button('Launch Balloons π'):
st.balloons() # plays a one-shot balloon animation
# st.snow β falling snowflakes animation
if col2.button('Make it Snow βοΈ'):
st.snow() # plays a one-shot snowflake animation
st.caption('Both st.balloons() and st.snow() trigger a celebratory animation once.')import streamlit as st
import pandas as pd
import numpy as np
st.set_page_config(page_title='KPI Dashboard', layout='wide')
st.title('Daily KPI Dashboard')
st.caption('Live metrics for standup β auto-refreshes on rerun')
# Simulate KPI data
np.random.seed(42)
kpis = {
'Daily Active Users': (12430, 342),
'Revenue ($)': (84201, 1205),
'New Signups': (87, 5),
'Tickets Resolved': (142, -8),
}
cols = st.columns(len(kpis))
for col, (name, (val, delta)) in zip(cols, kpis.items()):
col.metric(name, f'{val:,}', f'{delta:+,}')
st.divider()
dates = pd.date_range('2024-01-01', periods=30)
df = pd.DataFrame({'DAU': np.random.randint(10000, 15000, 30)}, index=dates)
st.line_chart(df, use_container_width=True)# Run with: streamlit run app.py
import streamlit as st
import pandas as pd
import numpy as np
from datetime import date
# TODO: Call st.set_page_config with page_title='My First App' and layout='wide'
# st.set_page_config(???)
# TODO: Add a st.header showing your name
# st.header(???)
# TODO: Show today's date as a metric card
# st.metric(label='Today', value=str(date.today()))
# TODO: Add a text_input for the user's name
# user_name = st.text_input('Enter your name')
# TODO: If len(user_name) > 3 show st.success, else st.warning
# if len(user_name) > 3:
# st.success(f'Welcome, {user_name}!')
# else:
# st.warning('Name must be longer than 3 characters.')
# TODO: Display a table of 5 random numbers
# df = pd.DataFrame({'Random Numbers': np.random.randint(1, 100, 5)})
# st.table(df)Display text, markdown, LaTeX equations, and formatted content in your app.
Text Display Functions
import streamlit as st
st.title('Title β Large header')
st.header('Header β Section title')
st.subheader('Subheader β Subsection')
st.write('st.write() is the Swiss Army knife β handles text, DataFrames, charts.')
st.markdown('**Bold**, *italic*, `code`, [link](https://streamlit.io)')
st.markdown(
'Custom colored text',
help='This tooltip appears on hover'
)
# Latex
st.latex(r'E = mc^2')
st.latex(r'\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}')
# Divider
st.divider()
st.caption('Small caption text for annotations')Formatted Data & JSON
import streamlit as st
# JSON viewer β interactive expandable tree
data = {
'user': {'name': 'Alice', 'age': 30},
'orders': [101, 102, 103],
'active': True
}
st.json(data)
# Preformatted text
st.text('Monospace text\nLine 2\nLine 3')
# Code with syntax highlighting
st.code(
'SELECT name, COUNT(*) FROM orders GROUP BY name ORDER BY 2 DESC',
language='sql'
)
# HTML (use with caution)
st.markdown('<p style="color:salmon">Custom HTML paragraph</p>', unsafe_allow_html=True)st.code Highlighting, st.latex & st.divider
import streamlit as st
st.title('Code, Math & Dividers')
# st.code supports many languages for syntax highlighting
st.subheader('Python snippet')
st.code(
'import numpy as np\n'
'arr = np.random.randn(100)\n'
'mean = arr.mean() # fast C-level operation',
language='python'
)
st.subheader('Bash command')
st.code('pip install streamlit pandas plotly', language='bash')
st.divider() # horizontal rule β cleaner than st.markdown('---')
st.subheader('LaTeX Math Equations')
st.latex(r'\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i') # mean formula
st.latex(r'\sigma = \sqrt{\frac{\sum(x_i - \bar{x})^2}{n}}') # std dev
st.latex(r'r = \frac{\sum(x_i-\bar{x})(y_i-\bar{y})}{\sqrt{\sum(x_i-\bar{x})^2\sum(y_i-\bar{y})^2}}')
st.divider()
st.caption('Use st.latex for any valid LaTeX expression β great for ML papers.')Multi-Language st.code, Inline Math & st.divider Layout
# Run: streamlit run app.py
import streamlit as st
st.title('Text, Code & Math Showcase')
# st.code with syntax highlighting across multiple languages
st.subheader('SQL Query')
st.code(
'SELECT customer_id, SUM(revenue) AS total\n'
'FROM orders\n'
'WHERE status = \'completed\'\n'
'GROUP BY customer_id\n'
'ORDER BY total DESC\n'
'LIMIT 10;',
language='sql'
)
# st.divider creates a clean horizontal separator
st.divider()
st.subheader('Machine Learning Formulas')
# Logistic regression sigmoid function
st.latex(r'\sigma(z) = \frac{1}{1 + e^{-z}}')
st.caption('Sigmoid / logistic function β squashes any value into (0, 1).')
# Cross-entropy loss
st.latex(r'\mathcal{L} = -\frac{1}{n}\sum_{i=1}^{n}\left[y_i\log(\hat{y}_i) + (1-y_i)\log(1-\hat{y}_i)\right]')
st.caption('Binary cross-entropy loss used in classification models.')
st.divider()
# Inline code via st.markdown backticks
st.markdown('Use `st.code()` for blocks and backticks in `st.markdown()` for inline code.')
st.markdown('Example: `df.groupby("col").agg({"val": "sum"})` aggregates by group.')import streamlit as st
st.title('Model Report: Customer Churn Predictor v2.1')
st.caption('Generated 2024-01-15 | Random Forest | 85% accuracy')
st.header('Executive Summary')
st.write(
'This model predicts which customers are at risk of churning within 30 days. '
'Trained on 50,000 historical records with 15 engineered features.'
)
st.header('Performance Metrics')
c1, c2, c3, c4 = st.columns(4)
c1.metric('Accuracy', '85.3%')
c2.metric('Precision', '82.1%')
c3.metric('Recall', '78.9%')
c4.metric('AUC-ROC', '0.91')
st.header('Top Features')
st.markdown(
'1. **recency_days** β days since last purchase\n'
'2. **support_calls** β number of support contacts\n'
'3. **monthly_spend** β average spend per month\n'
'4. **login_frequency** β logins in last 30 days'
)
st.info('Model retrained weekly. Next scheduled run: 2024-01-22.')# Run with: streamlit run app.py
import streamlit as st
st.set_page_config(page_title='DS Cheat Sheet', layout='wide')
# TODO: Add a st.title for the page
# st.title('Data Science Cheat Sheet')
# TODO: Add a st.header for the pandas section
# st.header('Pandas Quick Reference')
# TODO: Add a markdown table of 4 common pandas functions
# st.markdown("""
# | Function | Description |
# |---|---|
# | `df.head()` | First 5 rows |
# | `df.describe()` | Summary stats |
# | `df.groupby()` | Group and aggregate |
# | `df.merge()` | Join DataFrames |
# """)
# TODO: Show the mean formula using st.latex
# st.latex(r'\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i')
# TODO: Show a Python code snippet with st.code
# st.code('df.groupby("category")["sales"].sum().sort_values(ascending=False)', language='python')
# TODO: Add a st.caption
# st.caption('Tip: Use df.info() to quickly inspect dtypes and null counts.')
# TODO: Add one each of st.info, st.warning, st.error, st.success with a tip
# st.info('Use vectorized operations β avoid Python loops on DataFrames.')
# st.warning('???')
# st.error('???')
# st.success('???')Collect user input with buttons, sliders, text boxes, dropdowns, checkboxes, and more.
Basic Input Widgets
import streamlit as st
# Text inputs
name = st.text_input('Name', placeholder='Enter your name')
bio = st.text_area('Bio', height=100)
password = st.text_input('Password', type='password')
# Numeric
age = st.number_input('Age', min_value=1, max_value=120, value=25)
score = st.slider('Score', 0, 100, 50)
price_range = st.slider('Price Range ($)', 0, 1000, (100, 500))
# Selections
country = st.selectbox('Country', ['USA', 'UK', 'Canada', 'Australia'])
skills = st.multiselect('Skills', ['Python', 'SQL', 'ML', 'Spark'], default=['Python'])
# Boolean
agree = st.checkbox('I agree to terms')
dark = st.toggle('Dark mode')
if name:
st.write(f'Hello {name}! Age: {age}, Skills: {skills}')Date, Color & Radio Inputs
import streamlit as st
from datetime import date
# Date / time
birthday = st.date_input('Birthday', value=date(1990, 1, 1))
meeting_time = st.time_input('Meeting time')
# Radio buttons
plan = st.radio(
'Choose plan',
['Free', 'Pro ($9/mo)', 'Enterprise'],
horizontal=True
)
# Color picker
bg_color = st.color_picker('Brand color', '#4CAF50')
# File uploader (see dedicated section)
# st.file_uploader('Upload CSV', type=['csv'])
st.divider()
st.write('Selected plan:', plan)
st.write('Meeting at:', meeting_time)
st.markdown(f'Preview: <span style="background:{bg_color};padding:4px 12px;border-radius:4px">Brand</span>', unsafe_allow_html=True)st.color_picker, st.data_editor & st.camera_input
import streamlit as st
import pandas as pd
st.title('Advanced Input Widgets')
# color_picker β returns a hex string
st.subheader('Color Picker')
chosen = st.color_picker('Pick a highlight color', '#ff4b4b')
st.markdown(f'<div style="background:{chosen};height:40px;border-radius:6px;"></div>', unsafe_allow_html=True)
st.divider()
# data_editor β editable DataFrame widget
st.subheader('Editable DataFrame (st.data_editor)')
df = pd.DataFrame({'Task': ['Write tests', 'Review PR', 'Deploy'], 'Done': [True, False, False], 'Priority': [1, 2, 3]})
edited = st.data_editor(df, use_container_width=True, num_rows='dynamic')
done_count = edited['Done'].sum()
st.write(f'{done_count}/{len(edited)} tasks complete')
st.divider()
# camera_input β capture a photo from webcam (returns file-like object or None)
st.subheader('Camera Input (st.camera_input)')
st.write('Requires browser permission to access camera.')
photo = st.camera_input('Take a photo')
if photo:
st.image(photo, caption='Captured image', use_container_width=True)st.date_input, st.time_input & st.number_input with step
# Run: streamlit run app.py
import streamlit as st
from datetime import date, time
st.title('Date, Time & Number Inputs')
st.subheader('Date Input')
# date_input β returns a datetime.date object
# Pass a tuple (start, end) to create a date range picker
selected_date = st.date_input(
'Select a date',
value=date.today(),
min_value=date(2020, 1, 1),
max_value=date(2030, 12, 31),
format='YYYY-MM-DD' # display format in the widget
)
st.write(f'You selected: {selected_date} (weekday: {selected_date.strftime("%A")})')
st.divider()
st.subheader('Time Input')
# time_input β returns a datetime.time object, step in seconds
meeting = st.time_input('Meeting time', value=time(9, 0), step=900) # 15-min steps
st.write(f'Meeting at: {meeting.strftime("%I:%M %p")}')
st.divider()
st.subheader('Number Input with Step')
# step controls how much each +/- click changes the value
budget = st.number_input(
'Monthly budget ($)',
min_value=0.0,
max_value=100_000.0,
value=5_000.0,
step=500.0, # each click increments by $500
format='%.2f' # always show 2 decimal places
)
st.metric('Budget Selected', f'${budget:,.2f}')
st.caption('step=500.0 means +/- buttons move in $500 increments.')import streamlit as st
import pandas as pd
import numpy as np
st.title('Stock Screener')
with st.sidebar:
st.header('Filters')
sectors = st.multiselect('Sector', ['Tech', 'Finance', 'Health', 'Energy'], default=['Tech'])
pe_range = st.slider('P/E Ratio', 0, 100, (5, 40))
min_cap = st.number_input('Min Market Cap ($B)', value=1.0)
profitable = st.checkbox('Profitable only', value=True)
# Simulate stock data
np.random.seed(42)
n = 100
df = pd.DataFrame({
'ticker': [f'TICK{i}' for i in range(n)],
'sector': np.random.choice(['Tech','Finance','Health','Energy'], n),
'pe': np.random.uniform(2, 90, n).round(1),
'market_cap': np.random.exponential(50, n).round(2),
'profit': np.random.choice([True, False], n, p=[0.7, 0.3])
})
filtered = df[
(df['sector'].isin(sectors)) &
(df['pe'].between(*pe_range)) &
(df['market_cap'] >= min_cap) &
(df['profit'] == profitable if profitable else True)
]
st.write(f'Found **{len(filtered)}** matching stocks')
st.dataframe(filtered, use_container_width=True)# Run with: streamlit run app.py
import streamlit as st
st.set_page_config(page_title='BMI Calculator', layout='centered')
st.title('BMI Calculator')
# TODO: Add weight input (number_input, min_value=1, max_value=300, value=70, step=1)
# weight = st.number_input('Weight (kg)', min_value=1, max_value=300, value=70, step=1)
# TODO: Add height input in cm (number_input, min_value=50, max_value=250, value=170)
# height_cm = st.number_input('Height (cm)', min_value=50, max_value=250, value=170)
# TODO: Add a 'Calculate BMI' button
# if st.button('Calculate BMI'):
# height_m = height_cm / 100
# bmi = weight / (height_m ** 2)
# # TODO: Display BMI as st.metric
# st.metric(label='Your BMI', value=f'{bmi:.1f}')
# # TODO: Show a progress bar (clamp bmi to 0-100 scale)
# st.progress(min(int(bmi), 100))
# # TODO: Categorize and show appropriate message
# if bmi < 18.5:
# st.warning('Category: Underweight')
# elif bmi < 25:
# st.success('Category: Normal weight')
# elif bmi < 30:
# st.warning('Category: Overweight')
# else:
# st.error('Category: Obese')Show DataFrames, tables, metrics, and JSON with built-in Streamlit display components.
DataFrame & Table Display
import streamlit as st
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Carol', 'Dave', 'Eve'],
'Score': np.random.randint(60, 100, 5),
'Grade': ['A', 'B', 'A', 'C', 'B'],
'Passed': [True, True, True, False, True]
})
# Interactive sortable dataframe
st.subheader('Interactive DataFrame')
st.dataframe(df, use_container_width=True)
# Static table
st.subheader('Static Table')
st.table(df)
# With column config (v1.23+)
st.dataframe(
df,
column_config={
'Score': st.column_config.ProgressColumn('Score', min_value=0, max_value=100),
'Passed': st.column_config.CheckboxColumn('Passed')
}
)Metrics & Editable DataFrames
import streamlit as st
import pandas as pd
import numpy as np
# Metric cards with delta
c1, c2, c3 = st.columns(3)
c1.metric('Total Sales', '$1.2M', '+8.3%', delta_color='normal')
c2.metric('Avg Order Value', '$142', '-$3', delta_color='inverse')
c3.metric('Cart Abandonment', '68%', '+2%', delta_color='inverse')
st.divider()
# Editable dataframe
st.subheader('Editable Data')
data = pd.DataFrame({
'Product': ['Widget A', 'Widget B', 'Widget C'],
'Price': [29.99, 49.99, 99.99],
'Stock': [150, 80, 30]
})
edited = st.data_editor(data, use_container_width=True)
st.write('Total inventory value: $', (edited['Price'] * edited['Stock']).sum().round(2))column_config: NumberColumn, ProgressColumn & LinkColumn
import streamlit as st
import pandas as pd
import numpy as np
st.title('column_config Examples')
np.random.seed(7)
df = pd.DataFrame({
'Product': [f'Product {c}' for c in 'ABCDE'],
'Revenue': np.random.randint(5000, 80000, 5),
'Growth': np.random.uniform(-10, 40, 5).round(1),
'Docs': [
'https://docs.streamlit.io',
'https://pandas.pydata.org',
'https://numpy.org',
'https://plotly.com',
'https://scikit-learn.org',
]
})
st.dataframe(
df,
use_container_width=True,
column_config={
# NumberColumn: format revenue with $ sign and thousands separator
'Revenue': st.column_config.NumberColumn(
'Revenue ($)', format='$%d'
),
# ProgressColumn: visualise growth as a bar (0-100 range)
'Growth': st.column_config.ProgressColumn(
'Growth (%)', min_value=-10, max_value=40, format='%.1f%%'
),
# LinkColumn: render URLs as clickable hyperlinks
'Docs': st.column_config.LinkColumn('Documentation'),
}
)st.metric with delta & st.json Interactive Viewer
# Run: streamlit run app.py
import streamlit as st
st.title('Metrics & JSON Viewer')
st.subheader('st.metric with delta_color')
# delta_color='normal' β green for positive, red for negative (default)
# delta_color='inverse' β red for positive, green for negative (good for costs/errors)
# delta_color='off' β delta shown in grey with no color coding
c1, c2, c3, c4 = st.columns(4)
c1.metric('Revenue', '$84,201', '+$6,201', delta_color='normal')
c2.metric('Operating Cost','$31,400', '+$1,200', delta_color='inverse') # cost up = bad
c3.metric('Error Rate', '0.12%', '-0.03%', delta_color='inverse') # error down = good
c4.metric('NPS Score', '72', '+4', delta_color='off') # neutral display
st.divider()
st.subheader('st.json β interactive expandable tree')
# st.json renders any JSON-serialisable object as an interactive tree
api_response = {
'status': 'success',
'model': 'random_forest_v3',
'prediction': {'label': 'churn', 'probability': 0.87},
'feature_importance': {
'recency_days': 0.42,
'support_calls': 0.28,
'monthly_spend': 0.18,
'login_frequency': 0.12,
},
'metadata': {'version': '3.1.0', 'latency_ms': 14}
}
# expanded=1 opens the top level; set to True for fully expanded
st.json(api_response, expanded=1)import streamlit as st
import pandas as pd
import numpy as np
st.title('Inventory Management')
np.random.seed(0)
inventory = pd.DataFrame({
'SKU': [f'SKU-{i:04d}' for i in range(10)],
'Product': [f'Product {chr(65+i)}' for i in range(10)],
'Current Stock': np.random.randint(0, 200, 10),
'Reorder Level': np.random.randint(20, 50, 10),
'Unit Cost ($)': np.random.uniform(5, 100, 10).round(2)
})
edited = st.data_editor(
inventory,
column_config={
'Current Stock': st.column_config.NumberColumn(min_value=0),
'Unit Cost ($)': st.column_config.NumberColumn(format='$%.2f')
},
use_container_width=True
)
low_stock = edited[edited['Current Stock'] < edited['Reorder Level']]
if len(low_stock):
st.warning(f'{len(low_stock)} items below reorder level:')
st.dataframe(low_stock[['SKU','Product','Current Stock','Reorder Level']])
else:
st.success('All stock levels healthy!')# Run with: streamlit run app.py
import streamlit as st
import pandas as pd
import numpy as np
st.set_page_config(page_title='Sales Dashboard', layout='wide')
st.title('Sales Dashboard Table')
# TODO: Create a DataFrame of 20 sales records
# np.random.seed(42)
# products = [f'Product {i+1}' for i in range(20)]
# df = pd.DataFrame({
# 'Product': products,
# 'Revenue': np.random.randint(1000, 50000, 20),
# 'Units': np.random.randint(10, 500, 20),
# 'Growth': np.random.uniform(0, 100, 20).round(1),
# })
# TODO: Add a text_input to filter by product name
# search = st.text_input('Search by product name')
# if search:
# df = df[df['Product'].str.contains(search, case=False)]
# TODO: Display with st.dataframe and column_config
# st.dataframe(
# df,
# use_container_width=True,
# column_config={
# 'Revenue': st.column_config.NumberColumn('Revenue ($)', format='$%d'),
# 'Growth': st.column_config.ProgressColumn('Growth (%)', min_value=0, max_value=100, format='%.1f%%'),
# }
# )Embed Matplotlib, Plotly, Altair charts and use Streamlit's built-in chart functions.
Built-in Charts
import streamlit as st
import pandas as pd
import numpy as np
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=60)
df = pd.DataFrame({
'Sales': np.cumsum(np.random.randn(60)) + 100,
'Returns': np.cumsum(np.random.randn(60) * 0.3) + 10,
}, index=dates)
# Built-in charts (fast, simple)
st.line_chart(df)
st.area_chart(df)
st.bar_chart(df['Sales'])
# Scatter
scatter_data = pd.DataFrame({
'x': np.random.randn(50),
'y': np.random.randn(50)
})
st.scatter_chart(scatter_data)Plotly & Matplotlib in Streamlit
import streamlit as st
import plotly.express as px
import matplotlib.pyplot as plt
import numpy as np
# Plotly chart
df = px.data.gapminder().query('year == 2007')
fig = px.scatter(
df, x='gdpPercap', y='lifeExp', size='pop',
color='continent', hover_name='country',
log_x=True, title='GDP vs Life Expectancy (2007)'
)
st.plotly_chart(fig, use_container_width=True)
# Matplotlib chart
fig2, ax = plt.subplots()
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), label='sin(x)')
ax.plot(x, np.cos(x), label='cos(x)')
ax.legend()
ax.set_title('Trig Functions')
st.pyplot(fig2)st.plotly_chart with Custom Theme & use_container_width
import streamlit as st
import plotly.graph_objects as go
import numpy as np
st.title('Plotly Custom Theme')
# Generate data
np.random.seed(0)
x = np.arange(1, 13)
y1 = np.random.randint(30000, 80000, 12)
y2 = np.random.randint(20000, 60000, 12)
months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
fig = go.Figure()
fig.add_trace(go.Bar(name='2023', x=months, y=y1, marker_color='#ff4b4b'))
fig.add_trace(go.Bar(name='2024', x=months, y=y2, marker_color='#388bfd'))
# Apply a dark custom theme via layout
fig.update_layout(
title='Monthly Revenue Comparison',
barmode='group',
paper_bgcolor='#0d1117',
plot_bgcolor='#161b22',
font=dict(color='#e6edf3'),
legend=dict(bgcolor='#30363d'),
xaxis=dict(gridcolor='#30363d'),
yaxis=dict(gridcolor='#30363d', tickprefix='$'),
)
# use_container_width makes the chart fill the full column width
st.plotly_chart(fig, use_container_width=True)st.pyplot, st.plotly_chart & st.altair_chart
# Run: streamlit run app.py
import streamlit as st
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import plotly.express as px
import altair as alt
import pandas as pd
import numpy as np
st.title('Multi-Library Charts')
np.random.seed(42)
df = pd.DataFrame({'x': range(20), 'y': np.random.randn(20).cumsum(),
'group': np.random.choice(['A','B'], 20)})
# 1. Matplotlib via st.pyplot
st.subheader('Matplotlib')
fig, ax = plt.subplots(figsize=(7, 3))
ax.plot(df['x'], df['y'], color='steelblue', linewidth=2)
ax.set_title('Cumulative Random Walk')
st.pyplot(fig)
plt.close()
# 2. Plotly via st.plotly_chart
st.subheader('Plotly (Interactive)')
fig2 = px.scatter(df, x='x', y='y', color='group', title='Scatter by Group')
st.plotly_chart(fig2, use_container_width=True)
# 3. Altair via st.altair_chart
st.subheader('Altair (Declarative)')
chart = alt.Chart(df).mark_line(point=True).encode(
x='x:Q', y='y:Q', color='group:N'
).properties(title='Altair Line Chart', width=600)
st.altair_chart(chart, use_container_width=True)import streamlit as st
import plotly.express as px
import pandas as pd
import numpy as np
st.title('Sales Analytics Dashboard')
# Sidebar controls
with st.sidebar:
st.header('Filters')
n_days = st.slider('Days to show', 7, 90, 30)
chart_type = st.radio('Chart type', ['Line', 'Bar', 'Area'])
# Generate data
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=n_days)
df = pd.DataFrame({
'date': dates,
'revenue': np.random.normal(10000, 1500, n_days).cumsum() / 10,
'orders': np.random.randint(50, 200, n_days)
})
if chart_type == 'Line':
fig = px.line(df, x='date', y='revenue', title='Revenue Trend')
elif chart_type == 'Bar':
fig = px.bar(df, x='date', y='revenue', title='Daily Revenue')
else:
fig = px.area(df, x='date', y='revenue', title='Revenue Area')
st.plotly_chart(fig, use_container_width=True)# Run with: streamlit run app.py
import streamlit as st
import pandas as pd
import numpy as np
st.set_page_config(page_title='Stock Simulator', layout='wide')
st.title('Stock Price Simulator')
# TODO: Add sidebar sliders for volatility and drift
# with st.sidebar:
# st.header('Parameters')
# volatility = st.slider('Volatility', 0.01, 0.05, 0.02, step=0.005)
# drift = st.slider('Drift', -0.02, 0.02, 0.001, step=0.001)
# TODO: Add a 'Simulate' button
# if st.button('Simulate'):
# np.random.seed(None) # different each run
# n = 252
# S0 = 100.0
# # Geometric Brownian Motion
# daily_returns = np.exp((drift - 0.5 * volatility**2) +
# volatility * np.random.randn(n))
# prices = S0 * np.cumprod(daily_returns)
# df = pd.DataFrame({'Price': prices}, index=range(1, n+1))
# # TODO: Display as st.line_chart
# st.line_chart(df)
# # TODO: Show min, max, final as metric cards in 3 columns
# c1, c2, c3 = st.columns(3)
# c1.metric('Min Price', f'${prices.min():.2f}')
# c2.metric('Max Price', f'${prices.max():.2f}')
# c3.metric('Final Price', f'${prices[-1]:.2f}')Organize your app with columns for side-by-side content, tabs for navigation, and expanders for collapsible sections.
Columns & Expanders
import streamlit as st
import numpy as np
# Equal columns
col1, col2, col3 = st.columns(3)
with col1:
st.metric('Revenue', '$42K', '+5%')
st.line_chart(np.random.randn(20))
with col2:
st.metric('Users', '1,204', '+12')
st.bar_chart(np.random.randint(10, 100, 20))
with col3:
st.metric('Uptime', '99.9%', '+0.1%')
st.area_chart(np.random.randn(20).cumsum())
# Weighted columns [2:1 ratio]
left, right = st.columns([2, 1])
left.write('Wide column (2/3 width)')
right.write('Narrow (1/3 width)')
# Expander
with st.expander('Show raw data'):
st.write({'a': 1, 'b': 2, 'c': 3})Tabs
import streamlit as st
import pandas as pd
import numpy as np
st.title('Multi-Tab Dashboard')
tab1, tab2, tab3 = st.tabs(['Overview', 'Data', 'Settings'])
with tab1:
st.header('Overview')
c1, c2 = st.columns(2)
c1.metric('Total Users', '45,231', '+1,200')
c2.metric('Revenue', '$128K', '+8.2%')
st.line_chart(np.random.randn(30).cumsum())
with tab2:
st.header('Raw Data')
df = pd.DataFrame(np.random.randn(10, 4), columns=['A','B','C','D'])
st.dataframe(df)
with tab3:
st.header('Settings')
st.text_input('API Key', type='password')
st.selectbox('Theme', ['Light', 'Dark', 'Auto'])
st.toggle('Enable notifications', value=True)st.container with border=True & st.popover
import streamlit as st
st.title('Containers & Popovers')
# st.container with border=True draws a visible box around content
with st.container(border=True):
st.subheader('Bordered Container')
st.write('Use border=True to visually group related widgets.')
c1, c2 = st.columns(2)
c1.metric('Requests', '1,204', '+42')
c2.metric('Errors', '3', '-1')
st.divider()
# st.popover (Streamlit 1.31+) β a button that opens a floating panel
st.write('Click the button below to open a popover:')
with st.popover('Open settings'):
st.markdown('**Popover Settings**')
st.slider('Font size', 10, 24, 14)
st.selectbox('Language', ['English', 'French', 'Spanish'])
st.toggle('Dark mode', value=True)
st.divider()
# Nested containers for complex layouts
outer = st.container(border=True)
with outer:
st.write('Outer container')
inner = st.container(border=True)
with inner:
st.write('Inner container β containers can be nested')st.container, st.empty for Dynamic Updates & st.status
# Run: streamlit run app.py
import streamlit as st
import time
st.title('Dynamic Layout Demo')
# st.container β group elements logically
with st.container(border=True):
st.subheader('Metrics Panel')
c1, c2, c3 = st.columns(3)
c1.metric('Revenue', '$42,000', '+8%')
c2.metric('Users', '1,234', '+12%')
c3.metric('Errors', '3', '-2')
# st.empty β placeholder that can be updated
st.subheader('Countdown')
placeholder = st.empty()
if st.button('Start Countdown'):
for i in range(5, 0, -1):
placeholder.markdown(f'## β± {i}...')
time.sleep(1)
placeholder.success('Done! π')
# st.status β progress display for long operations
if st.button('Run Long Process'):
with st.status('Processing...', expanded=True) as status:
st.write('Step 1: Loading data...')
time.sleep(0.5)
st.write('Step 2: Running model...')
time.sleep(0.5)
st.write('Step 3: Generating report...')
time.sleep(0.3)
status.update(label='Complete!', state='complete')import streamlit as st
import plotly.express as px
import pandas as pd
import numpy as np
st.set_page_config(layout='wide')
st.title('Analytics Portal')
tab_ov, tab_rev, tab_usr = st.tabs(['Overview', 'Revenue', 'Users'])
np.random.seed(42)
with tab_ov:
cols = st.columns(4)
for col, (label, val, delta) in zip(cols, [
('MAU', '120K', '+5K'), ('MRR', '$48K', '+$3K'),
('NPS', '72', '+4'), ('Uptime', '99.95%', '+0.05%')
]):
col.metric(label, val, delta)
with tab_rev:
df_rev = pd.DataFrame({
'month': pd.date_range('2024-01', periods=12, freq='MS'),
'revenue': np.random.normal(40000, 5000, 12).cumsum()
})
st.plotly_chart(px.line(df_rev, x='month', y='revenue', title='Monthly Revenue'), use_container_width=True)
with tab_usr:
df_usr = pd.DataFrame({
'segment': ['Free', 'Pro', 'Enterprise'],
'users': [8000, 3500, 500]
})
st.plotly_chart(px.pie(df_usr, names='segment', values='users', title='User Segments'), use_container_width=True)# Run with: streamlit run app.py
import streamlit as st
st.set_page_config(page_title='Product Catalog', layout='wide')
st.title('Product Catalog')
electronics = [
{'name': 'Laptop Pro', 'price': 1299, 'desc': '15-inch, 16GB RAM, 512GB SSD'},
{'name': 'Wireless Mouse', 'price': 39, 'desc': 'Ergonomic, 12-month battery'},
{'name': 'Mechanical Keyboard', 'price': 129, 'desc': 'RGB backlit, tactile switches'},
]
clothing = [
{'name': 'Running Shoes', 'price': 89, 'desc': 'Lightweight, breathable mesh'},
{'name': 'Hoodie', 'price': 49, 'desc': 'Warm fleece, kangaroo pocket'},
{'name': 'Yoga Pants', 'price': 55, 'desc': '4-way stretch, moisture-wicking'},
]
# TODO: Create two tabs: 'Electronics' and 'Clothing'
# tab1, tab2 = st.tabs(['Electronics', 'Clothing'])
# TODO: In each tab, loop over the product list and render cards in 3 columns
# with tab1:
# cols = st.columns(3)
# for col, product in zip(cols, electronics):
# with col:
# st.subheader(product['name'])
# st.metric('Price', f"${product['price']}")
# with st.expander('Description'):
# st.write(product['desc'])
# if st.button('Add to Cart', key=product['name']):
# st.toast(f"{product['name']} added to cart!")
# TODO: Repeat for tab2 with the clothing listUse the sidebar for filters/navigation and session_state to persist data across reruns.
Sidebar Navigation
import streamlit as st
with st.sidebar:
st.title('Navigation')
page = st.radio('Go to', ['Home', 'Analytics', 'Settings'])
st.divider()
st.header('Filters')
date_range = st.date_input('Date range', [])
category = st.multiselect('Category', ['A', 'B', 'C', 'D'])
threshold = st.slider('Threshold', 0.0, 1.0, 0.5)
st.divider()
st.info(f'Page: {page}')
# Render page content based on selection
if page == 'Home':
st.title('Home Page')
st.write('Welcome!')
elif page == 'Analytics':
st.title('Analytics')
st.write(f'Category filter: {category}')
else:
st.title('Settings')Session State for Persistence
import streamlit as st
# Initialize session state
if 'count' not in st.session_state:
st.session_state.count = 0
if 'history' not in st.session_state:
st.session_state.history = []
st.title('Counter with History')
col1, col2, col3 = st.columns(3)
if col1.button('Increment +1'):
st.session_state.count += 1
st.session_state.history.append(st.session_state.count)
if col2.button('Decrement -1'):
st.session_state.count -= 1
st.session_state.history.append(st.session_state.count)
if col3.button('Reset'):
st.session_state.count = 0
st.session_state.history = []
st.metric('Current Count', st.session_state.count)
st.write('History:', st.session_state.history)session_state with on_change Callbacks & st.rerun()
import streamlit as st
st.title('Callbacks & st.rerun()')
# on_change callback β fires when the widget value changes
def on_name_change():
# Access new value via session_state key
st.session_state.greeting = f'Hello, {st.session_state.username}!'
if 'greeting' not in st.session_state:
st.session_state.greeting = 'Start typing your name...'
# The on_change callback runs BEFORE the rest of the script on each change
st.text_input('Your name', key='username', on_change=on_name_change)
st.info(st.session_state.greeting)
st.divider()
# st.rerun() β immediately restarts the script from the top
# Useful to force a refresh after a state mutation inside a button handler
if 'step' not in st.session_state:
st.session_state.step = 1
st.write(f'Step: {st.session_state.step}')
if st.button('Advance step'):
st.session_state.step += 1
st.rerun() # re-render immediately instead of waiting
if st.button('Reset step'):
st.session_state.step = 1
st.rerun()st.session_state with on_change Callbacks & st.rerun
# Run: streamlit run app.py
import streamlit as st
st.title('Session State with Callbacks')
# Initialize state
if 'theme' not in st.session_state:
st.session_state.theme = 'Light'
if 'font_size' not in st.session_state:
st.session_state.font_size = 14
if 'history' not in st.session_state:
st.session_state.history = []
# Callback function β runs BEFORE the widget returns
def on_theme_change():
st.session_state.history.append(
f'Theme changed to: {st.session_state.theme_select}'
)
st.session_state.theme = st.session_state.theme_select
st.selectbox(
'Theme', ['Light', 'Dark', 'High Contrast'],
key='theme_select',
on_change=on_theme_change
)
st.slider('Font size', 10, 24, key='font_size')
st.write(f'Active theme: **{st.session_state.theme}**')
st.write(f'Font size: **{st.session_state.font_size}px**')
if st.session_state.history:
st.subheader('Change History')
for item in st.session_state.history[-5:]:
st.write(f'β’ {item}')
if st.button('Reset All'):
for key in ['theme', 'font_size', 'history', 'theme_select']:
st.session_state.pop(key, None)
st.rerun() # immediately refresh the pageimport streamlit as st
if 'cart' not in st.session_state:
st.session_state.cart = {}
products = {
'Laptop': 999.99,
'Mouse': 29.99,
'Keyboard': 79.99,
'Monitor': 399.99,
'Headphones': 149.99
}
st.title('Online Store')
col1, col2 = st.columns([2, 1])
with col1:
st.header('Products')
for product, price in products.items():
pc1, pc2, pc3 = st.columns([3, 1, 1])
pc1.write(f'**{product}** β ${price}')
if pc2.button('Add', key=f'add_{product}'):
st.session_state.cart[product] = st.session_state.cart.get(product, 0) + 1
if pc3.button('Remove', key=f'rem_{product}'):
if product in st.session_state.cart:
del st.session_state.cart[product]
with col2:
st.header('Cart')
total = 0
for item, qty in st.session_state.cart.items():
subtotal = products[item] * qty
st.write(f'{item} x{qty} = ${subtotal:.2f}')
total += subtotal
st.divider()
st.metric('Total', f'${total:.2f}')# Run with: streamlit run app.py
import streamlit as st
QUESTIONS = [
{'q': 'What function starts a Streamlit app?', 'opts': ['st.start()', 'streamlit run', 'st.run()', 'main()'], 'ans': 1},
{'q': 'Which decorator caches data in Streamlit?', 'opts': ['@st.cache', '@st.cache_data', '@lru_cache', '@cache'], 'ans': 1},
{'q': 'How do you create 3 equal columns?', 'opts': ['st.layout(3)', 'st.columns(3)', 'st.cols(3)', 'st.grid(3)'], 'ans': 1},
{'q': 'What persists across reruns?', 'opts': ['local variables', 'st.session_state', 'global vars', 'st.memory'], 'ans': 1},
{'q': 'Which chart type does Streamlit NOT have built-in?', 'opts': ['line_chart', 'bar_chart', 'gantt_chart', 'scatter_chart'], 'ans': 2},
]
st.set_page_config(page_title='Quiz App', layout='centered')
st.title('Streamlit Quiz')
# TODO: Initialize session_state keys: q_index=0, score=0, finished=False
# if 'q_index' not in st.session_state:
# st.session_state.q_index = 0
# st.session_state.score = 0
# st.session_state.finished = False
# TODO: If not finished, show current question
# if not st.session_state.finished:
# q_data = QUESTIONS[st.session_state.q_index]
# st.subheader(f'Q{st.session_state.q_index + 1}: {q_data["q"]}')
# choice = st.radio('Choose:', q_data['opts'], key=f'q{st.session_state.q_index}')
# if st.button('Submit'):
# if q_data['opts'].index(choice) == q_data['ans']:
# st.session_state.score += 1
# st.session_state.q_index += 1
# if st.session_state.q_index >= len(QUESTIONS):
# st.session_state.finished = True
# st.rerun()
# TODO: Show final score and Restart button when finished
# else:
# st.success(f'Quiz complete! Score: {st.session_state.score}/{len(QUESTIONS)}')
# if st.button('Restart'):
# st.session_state.q_index = 0
# st.session_state.score = 0
# st.session_state.finished = False
# st.rerun()Let users upload CSV/images and offer processed results as downloadable files.
File Upload
import streamlit as st
import pandas as pd
st.title('CSV Analyzer')
uploaded = st.file_uploader('Upload a CSV file', type=['csv', 'xlsx'])
if uploaded is not None:
# Read based on type
if uploaded.name.endswith('.csv'):
df = pd.read_csv(uploaded)
else:
df = pd.read_excel(uploaded)
st.success(f'Loaded {len(df):,} rows x {df.shape[1]} cols')
col1, col2 = st.columns(2)
col1.subheader('Preview')
col1.dataframe(df.head())
col2.subheader('Summary Stats')
col2.dataframe(df.describe())
# Show missing values
nulls = df.isnull().sum()
if nulls.any():
st.warning('Missing values found:')
st.write(nulls[nulls > 0])
else:
st.info('Please upload a CSV to get started.')File Download
import streamlit as st
import pandas as pd
import numpy as np
st.title('Report Generator')
# Generate sample report
np.random.seed(42)
df = pd.DataFrame({
'Region': np.random.choice(['North','South','East','West'], 100),
'Sales': np.random.randint(1000, 50000, 100),
'Units': np.random.randint(10, 500, 100)
})
st.dataframe(df)
# Download as CSV
csv = df.to_csv(index=False)
st.download_button(
label='Download as CSV',
data=csv,
file_name='sales_report.csv',
mime='text/csv'
)
# Download as JSON
st.download_button(
label='Download as JSON',
data=df.to_json(orient='records'),
file_name='sales_report.json',
mime='application/json'
)Processing Multiple Uploaded Files & Zip Downloads
import streamlit as st
import pandas as pd
import io, zipfile
st.title('Multi-File Processor')
# Accept multiple files in one uploader
files = st.file_uploader(
'Upload one or more CSV files',
type='csv',
accept_multiple_files=True
)
if files:
summaries = {}
for f in files:
df = pd.read_csv(f)
st.subheader(f.name)
st.write(f'{len(df)} rows, {df.shape[1]} cols')
st.dataframe(df.head(3))
summaries[f.name.replace('.csv','_summary.csv')] = df.describe().to_csv()
# Build a zip file in memory containing all summary CSVs
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
for fname, content in summaries.items():
zf.writestr(fname, content)
st.download_button(
label=f'Download all {len(summaries)} summaries as ZIP',
data=zip_buffer.getvalue(),
file_name='summaries.zip',
mime='application/zip'
)
else:
st.info('Upload CSV files to begin.')st.camera_input & st.download_button with CSV
# Run: streamlit run app.py
import streamlit as st
import pandas as pd
import numpy as np
import io
st.title('File Operations')
# Generate sample data for download
np.random.seed(42)
df = pd.DataFrame({
'id': range(1, 21),
'name': [f'Product_{i}' for i in range(1, 21)],
'price': np.random.uniform(9.99, 99.99, 20).round(2),
'stock': np.random.randint(0, 500, 20),
})
st.subheader('Download Sample Data')
st.dataframe(df.head())
# st.download_button β download as CSV
csv_bytes = df.to_csv(index=False).encode('utf-8')
st.download_button(
label='π₯ Download as CSV',
data=csv_bytes,
file_name='products.csv',
mime='text/csv',
help='Download the full product list'
)
# Download as Excel (requires openpyxl)
try:
excel_buf = io.BytesIO()
df.to_excel(excel_buf, index=False, engine='openpyxl')
st.download_button(
'π₯ Download as Excel',
data=excel_buf.getvalue(),
file_name='products.xlsx',
mime='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
except ImportError:
st.info('Install openpyxl for Excel download')
# st.camera_input β capture photo from webcam
st.subheader('Camera Input')
photo = st.camera_input('Take a photo')
if photo:
st.image(photo, caption='Captured photo', use_container_width=True)
st.write(f'File size: {len(photo.getvalue()) / 1024:.1f} KB')import streamlit as st
import pandas as pd
import io
st.title('CSV Cleaner')
uploaded = st.file_uploader('Upload CSV', type='csv')
if uploaded:
df = pd.read_csv(uploaded)
st.subheader('Original Data')
st.write(f'Shape: {df.shape} | Nulls: {df.isnull().sum().sum()}')
st.dataframe(df.head())
st.subheader('Cleaning Options')
drop_nulls = st.checkbox('Drop rows with any nulls', value=True)
strip_spaces = st.checkbox('Strip whitespace from strings', value=True)
dedup = st.checkbox('Remove duplicate rows', value=True)
if st.button('Clean Data'):
cleaned = df.copy()
if drop_nulls:
cleaned = cleaned.dropna()
if strip_spaces:
for col in cleaned.select_dtypes('object').columns:
cleaned[col] = cleaned[col].str.strip()
if dedup:
cleaned = cleaned.drop_duplicates()
st.success(f'Cleaned: {len(df) - len(cleaned)} rows removed')
st.dataframe(cleaned.head())
st.download_button('Download Cleaned CSV', cleaned.to_csv(index=False), 'cleaned.csv', 'text/csv')# Run with: streamlit run app.py
import streamlit as st
import pandas as pd
st.set_page_config(page_title='CSV Analyzer', layout='wide')
st.title('CSV Analyzer')
# TODO: Add a file uploader that accepts CSV files
# uploaded = st.file_uploader('Upload a CSV file', type=['csv'])
# TODO: If no file uploaded, show st.info with instructions
# if uploaded is None:
# st.info('Please upload a CSV file to get started.')
# else:
# # TODO: Show file name and size
# st.write(f'**File:** {uploaded.name} | **Size:** {uploaded.size:,} bytes')
# df = pd.read_csv(uploaded)
# # TODO: Show first 5 rows with st.dataframe
# st.subheader('Preview (first 5 rows)')
# st.dataframe(df.head())
# # TODO: Show column types with st.table
# st.subheader('Column Types')
# st.table(df.dtypes.rename('dtype').reset_index().rename(columns={'index':'Column'}))
# # TODO: Show df.describe() with st.dataframe
# st.subheader('Basic Statistics')
# desc = df.describe()
# st.dataframe(desc)
# # TODO: Add a download button for the describe() output as CSV
# st.download_button('Download Stats as CSV', desc.to_csv(), 'stats.csv', 'text/csv')Use forms to batch inputs and st.cache_data / st.cache_resource to speed up expensive operations.
Forms
import streamlit as st
st.title('Contact Form')
with st.form('contact_form'):
st.subheader('Send us a message')
name = st.text_input('Full Name *')
email = st.text_input('Email *')
subject = st.selectbox('Subject', ['Support', 'Sales', 'Billing', 'Other'])
message = st.text_area('Message *', height=150)
priority = st.radio('Priority', ['Low', 'Medium', 'High'], horizontal=True)
submitted = st.form_submit_button('Send Message')
if submitted:
if not name or not email or not message:
st.error('Please fill in all required fields.')
else:
st.success(f'Thank you {name}! We will respond to {email} shortly.')
st.json({'name': name, 'email': email, 'subject': subject, 'priority': priority})Caching with st.cache_data
import streamlit as st
import pandas as pd
import time
# Cache data loading β only runs once, then returns cached result
@st.cache_data
def load_data(n_rows: int = 10000):
time.sleep(2) # Simulate slow database query
import numpy as np
np.random.seed(42)
return pd.DataFrame({
'id': range(n_rows),
'value': np.random.randn(n_rows),
'category': np.random.choice(['A','B','C'], n_rows)
})
# Cache model loading β use cache_resource for non-serializable objects
@st.cache_resource
def load_model():
from sklearn.ensemble import RandomForestClassifier
# Returns same object on every call (no copy)
return RandomForestClassifier()
with st.spinner('Loading data...'):
df = load_data()
st.success(f'Loaded {len(df):,} rows (cached after first load)')
st.dataframe(df.head())st.cache_resource for ML Models & cache_data vs cache_resource
import streamlit as st
import time
st.title('cache_data vs cache_resource')
# --- st.cache_data ---
# - For data (DataFrames, lists, dicts, JSON)
# - Returns a NEW COPY each call (safe for mutations)
# - Serializes to disk/memory; works across sessions
@st.cache_data
def fetch_records(n: int):
print('fetch_records called') # runs only on cache miss
import numpy as np, pandas as pd
time.sleep(1)
return pd.DataFrame({'val': np.random.randn(n)})
# --- st.cache_resource ---
# - For shared resources: DB connections, ML models, tokenizers
# - Returns the SAME object (shared across all users / sessions)
# - NOT serialized β object lives in server memory
@st.cache_resource
def load_model():
print('load_model called') # runs only once per server restart
from sklearn.ensemble import RandomForestClassifier
import numpy as np
clf = RandomForestClassifier(n_estimators=10, random_state=0)
X = np.random.randn(200, 4)
y = (X[:, 0] > 0).astype(int)
clf.fit(X, y)
return clf
st.subheader('cache_data β returns copies')
df = fetch_records(500)
st.write(f'Loaded {len(df)} rows')
st.subheader('cache_resource β shared model')
model = load_model()
st.success(f'Model ready: {type(model).__name__} (shared, loaded once)')
st.info(
'Key difference: cache_data copies data so each caller is isolated. '
'cache_resource shares the same object β ideal for heavy models.'
)@st.cache_resource for Models & Connections
# Run: streamlit run app.py
import streamlit as st
import time
# @st.cache_resource β for expensive global resources (models, DB connections)
# Unlike @st.cache_data, resource is shared across all users/sessions
@st.cache_resource
def load_model(model_name: str):
"""Loads a model once per app lifetime β not per user session."""
st.write(f'Loading {model_name}...') # only shows on first load
time.sleep(2) # simulate slow model load
return {'name': model_name, 'weights': list(range(100)), 'loaded_at': time.time()}
@st.cache_data(ttl=60) # expires every 60 seconds
def fetch_live_data(endpoint: str):
"""Re-fetches data every 60s automatically."""
time.sleep(0.5) # simulate API call
import numpy as np
np.random.seed(int(time.time()) % 100)
return {'endpoint': endpoint, 'value': round(np.random.uniform(95, 105), 2)}
st.title('Resource & Data Caching')
model_name = st.selectbox('Select model', ['bert-base', 'gpt2', 'roberta'])
model = load_model(model_name)
st.success(f'Model "{model["name"]}" ready (loaded at {model["loaded_at"]:.0f})')
st.subheader('Live Data (refreshes every 60s)')
data = fetch_live_data('/api/sensor/temperature')
st.metric('Temperature', f"{data['value']}Β°C")
if st.button('Force refresh'):
st.cache_data.clear()
st.rerun()import streamlit as st
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
@st.cache_data
def generate_dataset(n=5000):
np.random.seed(42)
X = np.random.randn(n, 5)
y = (X[:, 0] + X[:, 1] > 0).astype(int)
return X, y
@st.cache_resource
def train_model():
X, y = generate_dataset()
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_scaled, y)
return model, scaler
st.title('Cached ML App')
model, scaler = train_model()
st.success('Model ready! (cached β no retraining on rerun)')
st.subheader('Make a prediction')
features = [st.slider(f'Feature {i+1}', -3.0, 3.0, 0.0) for i in range(5)]
if st.button('Predict'):
X_input = scaler.transform([features])
pred = model.predict(X_input)[0]
prob = model.predict_proba(X_input)[0]
st.metric('Prediction', 'Positive' if pred else 'Negative')
st.write(f'Confidence: {max(prob):.1%}')# Run with: streamlit run app.py
import streamlit as st
import pandas as pd
import numpy as np
from datetime import date, timedelta
st.set_page_config(page_title='Data Fetcher', layout='wide')
st.title('Data Fetcher with Cache & Form')
# TODO: Define a @st.cache_data function that creates 1000-row DataFrame
# It should print 'Fetching data...' so you can see caching in action
# @st.cache_data
# def get_data():
# print('Fetching data...')
# np.random.seed(0)
# base = date(2024, 1, 1)
# return pd.DataFrame({
# 'date': [base + timedelta(days=i % 365) for i in range(1000)],
# 'category': np.random.choice(['A', 'B', 'C', 'D'], 1000),
# **{f'col_{i}': np.random.randn(1000) for i in range(8)},
# })
# df = get_data()
# st.write(f'Loaded {len(df)} rows (cached)')
# TODO: Add a st.form with date_input range, category multiselect, and submit button
# with st.form('filter_form'):
# start = st.date_input('Start date', date(2024, 1, 1))
# end = st.date_input('End date', date(2024, 12, 31))
# cats = st.multiselect('Categories', ['A','B','C','D'], default=['A','B'])
# submitted = st.form_submit_button('Filter')
# TODO: On submit, filter and display results + download button
# if submitted:
# filtered = df[(df['date'] >= start) & (df['date'] <= end) & (df['category'].isin(cats))]
# st.write(f'{len(filtered)} rows match')
# st.dataframe(filtered.head(20))
# st.download_button('Download filtered CSV', filtered.to_csv(index=False), 'filtered.csv', 'text/csv')Structure larger apps with multiple pages and deploy to Streamlit Community Cloud for free.
Multi-Page App Structure
# Multi-page apps: create a 'pages/' folder next to your main app.py
#
# File structure:
# my_app/
# app.py <- main entry point
# pages/
# 1_Overview.py
# 2_Analytics.py
# 3_Settings.py
#
# Streamlit auto-creates sidebar navigation from this structure.
# app.py (home page)
import streamlit as st
st.set_page_config(page_title='My App', layout='wide')
st.title('Welcome to My App')
st.write('Use the sidebar to navigate between pages.')
# pages/1_Overview.py
# import streamlit as st
# st.title('Overview Page')
# st.write('This is the overview page.')
# Shared state across pages
if 'user' not in st.session_state:
st.session_state.user = 'Guest'
st.write(f'Logged in as: {st.session_state.user}')Progress & Status Indicators
import streamlit as st
import time
st.title('Long-Running Task Demo')
if st.button('Run Analysis'):
# Progress bar
progress = st.progress(0, text='Starting...')
status = st.empty()
steps = ['Loading data', 'Cleaning', 'Training model', 'Evaluating', 'Done!']
for i, step in enumerate(steps):
pct = int((i + 1) / len(steps) * 100)
progress.progress(pct, text=f'Step {i+1}/{len(steps)}: {step}')
status.info(f'Currently: {step}...')
time.sleep(0.5) # Simulate work
progress.empty()
status.empty()
st.success('Analysis complete!')
st.balloons()
# Spinner for indeterminate wait
if st.button('Quick Query'):
with st.spinner('Fetching data...'):
time.sleep(1)
st.write('Data loaded!')st.navigation API & st.page_link for In-App Navigation
# Modern multi-page approach using st.navigation (Streamlit 1.36+)
# Place this in app.py β pages are defined as Page objects
# === app.py ===
import streamlit as st
home = st.Page('pages/home.py', title='Home', icon='π ', default=True)
analytics = st.Page('pages/analytics.py', title='Analytics', icon='π')
settings = st.Page('pages/settings.py', title='Settings', icon='βοΈ')
# st.navigation replaces the auto-generated sidebar nav from the pages/ folder
pg = st.navigation([home, analytics, settings])
pg.run()
# ---------------------------------------------------------------
# st.page_link β renders a clickable link to another page
# Works in BOTH the pages/ folder approach and st.navigation
# Place these anywhere in your page files for in-app navigation
# ---------------------------------------------------------------
# Example: add to sidebar in any page
# with st.sidebar:
# st.page_link('app.py', label='Home', icon='π ')
# st.page_link('pages/analytics.py', label='Analytics', icon='π')
# st.page_link('pages/settings.py', label='Settings', icon='βοΈ')
# Compared to old approach (still valid):
# Just drop .py files in pages/ folder and Streamlit auto-generates sidebar nav
# pages/1_Home.py, pages/2_Analytics.py β number prefix controls orderst.navigation with st.Page & Page-Specific State
# Structure: create these files:
# app.py (entry point), pages/home.py, pages/data.py, pages/settings.py
# --- app.py ---
import streamlit as st
# New navigation API (Streamlit >= 1.36)
home = st.Page('pages/home.py', title='Home', icon='π ', default=True)
data = st.Page('pages/data.py', title='Data', icon='π')
settings = st.Page('pages/settings.py', title='Settings', icon='βοΈ')
pg = st.navigation({'Main': [home, data], 'Config': [settings]})
pg.run()
# --- pages/home.py ---
# import streamlit as st
# st.title('Home π ')
# st.write('Welcome! Use the sidebar to navigate.')
# if 'visit_count' not in st.session_state:
# st.session_state.visit_count = 0
# st.session_state.visit_count += 1
# st.metric('Page visits this session', st.session_state.visit_count)
# --- pages/data.py ---
# import streamlit as st, pandas as pd, numpy as np
# st.title('Data π')
# @st.cache_data
# def load(): return pd.DataFrame({'x': range(10), 'y': np.random.randn(10)})
# st.dataframe(load())
# --- pages/settings.py ---
# import streamlit as st
# st.title('Settings βοΈ')
# theme = st.radio('Theme', ['Light', 'Dark'])
# st.session_state['theme'] = theme
# if st.button('Switch to Data page'):
# st.switch_page('pages/data.py')# Complete production-ready app skeleton
# File: app.py
import streamlit as st
st.set_page_config(
page_title='ML Platform',
page_icon='π€',
layout='wide'
)
# Simulated auth
if 'authenticated' not in st.session_state:
st.session_state.authenticated = False
if not st.session_state.authenticated:
st.title('ML Platform β Login')
with st.form('login'):
username = st.text_input('Username')
password = st.text_input('Password', type='password')
if st.form_submit_button('Login'):
if username == 'admin' and password == 'demo123':
st.session_state.authenticated = True
st.session_state.user = username
st.rerun()
else:
st.error('Invalid credentials')
else:
with st.sidebar:
st.write(f'Logged in as **{st.session_state.user}**')
if st.button('Logout'):
st.session_state.authenticated = False
st.rerun()
st.title('ML Platform Dashboard')
tab1, tab2 = st.tabs(['Predict', 'Monitor'])
with tab1:
st.write('Upload data and run predictions here.')
with tab2:
st.write('Monitor model performance and drift here.')# Run with: streamlit run app.py
# File structure:
# app.py
# pages/
# 1_analytics.py
# 2_settings.py
# ============================================================
# FILE: app.py (home page β run this with streamlit run app.py)
# ============================================================
import streamlit as st
st.set_page_config(page_title='My Multi-Page App', layout='wide')
# TODO: Add sidebar navigation links using st.page_link
# with st.sidebar:
# st.title('Navigation')
# st.page_link('app.py', label='Home', icon='π ')
# st.page_link('pages/1_analytics.py', label='Analytics', icon='π')
# st.page_link('pages/2_settings.py', label='Settings', icon='βοΈ')
# TODO: Add home page content
# st.title('Welcome Home')
# st.write('Use the sidebar to navigate.')
# ============================================================
# FILE: pages/1_analytics.py
# ============================================================
# import streamlit as st
# import numpy as np
# with st.sidebar:
# st.title('Navigation')
# st.page_link('app.py', label='Home', icon='π ')
# st.page_link('pages/1_analytics.py', label='Analytics', icon='π')
# st.page_link('pages/2_settings.py', label='Settings', icon='βοΈ')
# st.title('Analytics')
# TODO: Show a line chart of random data
# st.line_chart(np.random.randn(30).cumsum())
# ============================================================
# FILE: pages/2_settings.py
# ============================================================
# import streamlit as st
# with st.sidebar:
# st.title('Navigation')
# st.page_link('app.py', label='Home', icon='π ')
# st.page_link('pages/1_analytics.py', label='Analytics', icon='π')
# st.page_link('pages/2_settings.py', label='Settings', icon='βοΈ')
# st.title('Settings')
# TODO: Add a form with a text_input, selectbox, and toggle β show submitted values
# with st.form('settings_form'):
# username = st.text_input('Display name')
# theme = st.selectbox('Theme', ['Light', 'Dark', 'Auto'])
# notifs = st.toggle('Enable notifications', value=True)
# if st.form_submit_button('Save'):
# st.success(f'Saved: {username}, {theme}, notifications={notifs}')Implement login/logout flows with session state, user role management, and per-user data isolation in Streamlit applications.
Basic Login with Session State
# app.py β Run with: streamlit run app.py
import streamlit as st
# User database (in practice: use a real DB)
USERS = {
'alice': {'password': 'pass123', 'role': 'admin'},
'bob': {'password': 'pass456', 'role': 'viewer'},
}
def login(username, password):
user = USERS.get(username)
return user if user and user['password'] == password else None
if 'logged_in' not in st.session_state:
st.session_state.logged_in = False
st.session_state.username = None
st.session_state.role = None
if not st.session_state.logged_in:
st.title('Login')
with st.form('login_form'):
username = st.text_input('Username')
password = st.text_input('Password', type='password')
submitted = st.form_submit_button('Login')
if submitted:
user = login(username, password)
if user:
st.session_state.logged_in = True
st.session_state.username = username
st.session_state.role = user['role']
st.rerun()
else:
st.error('Invalid credentials')
else:
st.title(f'Welcome, {st.session_state.username}!')
st.write(f'Role: {st.session_state.role}')
if st.session_state.role == 'admin':
st.success('Admin panel available')
if st.button('Logout'):
st.session_state.logged_in = False
st.rerun()Role-Based Access Control
# app.py β Role-based view
import streamlit as st
import pandas as pd
import numpy as np
def require_role(required_role):
role = st.session_state.get('role', 'viewer')
roles = ['viewer', 'editor', 'admin']
if roles.index(role) < roles.index(required_role):
st.error(f'Access denied. Requires {required_role} role.')
st.stop()
# Simulate login for demo
if 'role' not in st.session_state:
st.session_state.role = st.selectbox('Demo: select role', ['viewer','editor','admin'])
st.title('Dashboard')
st.write(f'Your role: **{st.session_state.role}**')
# Viewer: can see charts
np.random.seed(42)
df = pd.DataFrame({'x': range(20), 'y': np.random.randn(20).cumsum()})
st.line_chart(df.set_index('x'))
# Editor: can upload data
if st.session_state.role in ('editor', 'admin'):
require_role('editor')
with st.expander('Upload Data (Editor+)'):
file = st.file_uploader('CSV', type='csv')
if file: st.dataframe(pd.read_csv(file).head())
# Admin: can see user management
if st.session_state.role == 'admin':
require_role('admin')
with st.expander('Admin: User Management'):
st.write('User list would appear here')Persistent Session State & st.query_params
# Demonstrate session_state persistence and URL params
import streamlit as st
# Initialize state
if 'counter' not in st.session_state:
st.session_state.counter = 0
if 'history' not in st.session_state:
st.session_state.history = []
st.title('Session State Demo')
col1, col2, col3 = st.columns(3)
with col1:
if st.button('+1'): st.session_state.counter += 1; st.session_state.history.append('+')
with col2:
if st.button('-1'): st.session_state.counter -= 1; st.session_state.history.append('-')
with col3:
if st.button('Reset'): st.session_state.counter = 0; st.session_state.history = []
st.metric('Counter', st.session_state.counter)
st.write('History:', ''.join(st.session_state.history[-10:]))
# URL query params for shareable state
params = st.query_params
if 'tab' not in params:
st.query_params.tab = 'home'
st.write(f'Current tab (from URL): {st.query_params.get("tab", "home")}')
tab = st.selectbox('Navigate to', ['home', 'analytics', 'settings'])
if st.button('Update URL'):
st.query_params.tab = tabstreamlit-authenticator Library
# Using streamlit-authenticator for production auth
# pip install streamlit-authenticator
try:
import streamlit_authenticator as stauth
import yaml
config = {
'credentials': {
'usernames': {
'alice': {'name': 'Alice Admin', 'email': 'alice@co.com',
'password': stauth.Hasher(['pass123']).generate()[0]},
'bob': {'name': 'Bob Viewer', 'email': 'bob@co.com',
'password': stauth.Hasher(['pass456']).generate()[0]},
}
},
'cookie': {'name': 'app_cookie', 'key': 'secret_key_abc', 'expiry_days': 1}
}
authenticator = stauth.Authenticate(
config['credentials'], config['cookie']['name'],
config['cookie']['key'], config['cookie']['expiry_days']
)
name, auth_status, username = authenticator.login('Login', 'main')
if auth_status:
authenticator.logout('Logout', 'main')
import streamlit as st
st.write(f'Welcome *{name}*')
elif auth_status is False:
import streamlit as st
st.error('Invalid credentials')
except ImportError:
print('pip install streamlit-authenticator')
print('Features: bcrypt hashing, JWT cookies, forgot password flow')# Minimal auth + role-gated dashboard
import streamlit as st
import pandas as pd
import numpy as np
USERS = {
'admin@co.com': {'password': 'admin123', 'role': 'admin', 'name': 'Admin User'},
'analyst@co.com': {'password': 'view456', 'role': 'viewer', 'name': 'Jane Analyst'},
}
def init_state():
for k, v in [('auth', False), ('user', None), ('role', None)]:
if k not in st.session_state: st.session_state[k] = v
init_state()
if not st.session_state.auth:
st.title('Secure Analytics Dashboard')
with st.form('auth'):
email = st.text_input('Email')
pwd = st.text_input('Password', type='password')
if st.form_submit_button('Login'):
u = USERS.get(email)
if u and u['password'] == pwd:
st.session_state.update({'auth': True, 'user': u['name'], 'role': u['role']})
st.rerun()
else: st.error('Wrong credentials')
else:
st.sidebar.write(f'User: {st.session_state.user}')
if st.sidebar.button('Logout'): [st.session_state.pop(k) for k in ['auth','user','role']]; st.rerun()
st.title('Dashboard')
np.random.seed(0)
df = pd.DataFrame({'month': range(12), 'revenue': np.random.exponential(10000, 12).cumsum()})
st.line_chart(df.set_index('month'))
if st.session_state.role == 'admin':
st.subheader('Admin: Upload Data')
f = st.file_uploader('CSV file', type='csv')
if f: st.dataframe(pd.read_csv(f))# app.py β Multi-role gated dashboard
import streamlit as st
import pandas as pd
import numpy as np
USERS = {
'viewer': {'password': 'view123', 'role': 'viewer'},
'admin': {'password': 'admin123', 'role': 'admin'},
}
# TODO: Initialize session_state (logged_in, username, role)
# TODO: Show login form if not logged_in
# TODO: Validate credentials and set session_state on success
# TODO: Show viewer dashboard (a line chart) for all logged-in users
# TODO: Show admin panel (a settings form) only for role='admin'
# TODO: Add a Logout button that clears session_state and reruns
Connect Streamlit apps to SQLite, PostgreSQL, and other databases using st.connection, SQLAlchemy, and efficient caching with st.cache_data.
SQLite with st.cache_data
import streamlit as st
import sqlite3
import pandas as pd
import numpy as np
@st.cache_resource
def get_connection():
conn = sqlite3.connect(':memory:', check_same_thread=False)
# Seed with sample data
np.random.seed(42)
n = 1000
df = pd.DataFrame({
'order_id': range(n),
'product': np.random.choice(['laptop','phone','tablet'], n),
'region': np.random.choice(['NA','EU','APAC'], n),
'revenue': np.random.exponential(200, n).round(2),
'date': pd.date_range('2024-01-01', periods=n, freq='h').strftime('%Y-%m-%d'),
})
df.to_sql('orders', conn, if_exists='replace', index=False)
return conn
@st.cache_data(ttl=60)
def query_revenue(region=None):
conn = get_connection()
if region and region != 'All':
return pd.read_sql('SELECT * FROM orders WHERE region=?', conn, params=[region])
return pd.read_sql('SELECT * FROM orders', conn)
st.title('Sales Dashboard')
region = st.selectbox('Region', ['All', 'NA', 'EU', 'APAC'])
df = query_revenue(region)
st.metric('Total Revenue', f"${df['revenue'].sum():,.0f}")
st.metric('Orders', len(df))
st.bar_chart(df.groupby('product')['revenue'].sum())st.connection with SQLite
# Streamlit 1.28+ built-in connection API
import streamlit as st
import pandas as pd
import numpy as np
# Simulate the connection pattern (without running Streamlit)
print('st.connection API usage:')
print('''
# In your Streamlit app:
import streamlit as st
# .streamlit/secrets.toml:
# [connections.mydb]
# url = "sqlite:///data/sales.db"
conn = st.connection("mydb", type="sql")
# Query with caching (ttl=300 seconds)
@st.cache_data(ttl=300)
def load_orders():
return conn.query("SELECT * FROM orders WHERE revenue > 100")
df = load_orders()
st.dataframe(df)
''')
# SQLAlchemy equivalent (runs standalone)
import sqlite3
conn = sqlite3.connect(':memory:')
np.random.seed(42)
pd.DataFrame({
'product': np.random.choice(['A','B','C'], 100),
'revenue': np.random.exponential(100, 100).round(2)
}).to_sql('orders', conn, index=False)
result = pd.read_sql('SELECT product, SUM(revenue) as total FROM orders GROUP BY product', conn)
print('\nQuery result:')
print(result)Query Cache & Invalidation
import streamlit as st
import pandas as pd
import numpy as np
import time
@st.cache_data(ttl=30, show_spinner='Fetching data...')
def fetch_live_data(ticker: str):
"""Cached function β re-runs at most every 30 seconds."""
np.random.seed(hash(ticker) % 2**31)
time.sleep(0.1) # simulate DB query
n = 100
return pd.DataFrame({
'time': pd.date_range('2024-01-01', periods=n, freq='h'),
'price': 100 + np.cumsum(np.random.randn(n) * 2),
'volume': np.random.exponential(1000, n).astype(int),
})
st.title('Live Data with Cache')
ticker = st.selectbox('Ticker', ['AAPL', 'GOOG', 'MSFT'])
df = fetch_live_data(ticker)
st.line_chart(df.set_index('time')['price'])
st.caption(f'Data cached for 30s | {len(df)} rows loaded')
if st.button('Clear cache & refresh'):
st.cache_data.clear()
st.rerun()
# Show cache info
with st.expander('Cache info'):
st.code('@st.cache_data(ttl=30) # expires after 30 seconds')
st.write('Use ttl= to control expiry. Call st.cache_data.clear() to invalidate.')Real-time Data with st.empty & auto-refresh
import streamlit as st
import pandas as pd
import numpy as np
import time
st.title('Real-time Dashboard')
# Simulated DB query that returns latest N rows
def get_latest_readings(n=20):
np.random.seed(int(time.time()) % 1000)
return pd.DataFrame({
'sensor': np.random.choice(['CPU','GPU','RAM'], n),
'value': np.random.uniform(20, 95, n).round(1),
'ts': pd.Timestamp.now(),
})
# Auto-refresh every 2 seconds for N iterations
placeholder = st.empty()
chart_placeholder = st.empty()
history = pd.DataFrame(columns=['cpu','gpu','ram'])
for i in range(5): # In production: while True
df = get_latest_readings(10)
avgs = df.groupby('sensor')['value'].mean()
new_row = pd.DataFrame({'cpu': [avgs.get('CPU',0)], 'gpu': [avgs.get('GPU',0)], 'ram': [avgs.get('RAM',0)]})
history = pd.concat([history, new_row], ignore_index=True)
with placeholder.container():
cols = st.columns(3)
for col, (name, val) in zip(cols, avgs.items()):
col.metric(name, f'{val:.1f}%')
chart_placeholder.line_chart(history.tail(20))
time.sleep(0.5)import streamlit as st
import pandas as pd
import numpy as np
import sqlite3
@st.cache_resource
def init_db():
conn = sqlite3.connect(':memory:', check_same_thread=False)
np.random.seed(0)
n = 5000
pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=n, freq='h').strftime('%Y-%m-%d'),
'region': np.random.choice(['NA','EU','APAC'], n),
'product': np.random.choice([f'P{i}' for i in range(20)], n),
'revenue': np.random.exponential(500, n).round(2),
'units': np.random.randint(1, 100, n),
}).to_sql('sales', conn, index=False)
return conn
@st.cache_data(ttl=300)
def load_sales(region, date_from, date_to):
conn = init_db()
q = 'SELECT * FROM sales WHERE region=? AND date BETWEEN ? AND ?'
return pd.read_sql(q, conn, params=[region, date_from, date_to])
st.title('Sales Analytics')
col1, col2, col3 = st.columns(3)
region = col1.selectbox('Region', ['NA','EU','APAC'])
date_from = col2.text_input('From', '2024-01-01')
date_to = col3.text_input('To', '2024-06-30')
if st.button('Refresh'): st.cache_data.clear()
df = load_sales(region, date_from, date_to)
st.metric('Revenue', f"${df['revenue'].sum():,.0f}")
st.dataframe(df.head(50))
st.download_button('Export CSV', df.to_csv(index=False), 'sales.csv', 'text/csv')import streamlit as st
import sqlite3
import pandas as pd
import numpy as np
# TODO: @st.cache_resource β create SQLite in-memory DB with 2000 rows
# TODO: @st.cache_data(ttl=60) β query function with region filter
# TODO: Selectbox for region
# TODO: st.metric for total revenue, order count, avg revenue
# TODO: st.bar_chart for revenue by product
# TODO: 'Clear cache' button that calls st.cache_data.clear()
Inject custom HTML/CSS/JavaScript into Streamlit apps, build custom chart components, and create polished UIs with themes and markdown styling.
Custom HTML with st.markdown & st.components.v1
import streamlit as st
st.title('Custom Styling')
# Custom CSS via st.markdown
st.markdown('''
<style>
.custom-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
padding: 20px;
color: white;
text-align: center;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.metric-value {
font-size: 2.5rem;
font-weight: 700;
margin: 8px 0;
}
.metric-label {
font-size: 0.9rem;
opacity: 0.85;
}
</style>
''', unsafe_allow_html=True)
st.markdown('''
<div class="custom-card">
<div class="metric-label">Total Revenue</div>
<div class="metric-value">$2.4M</div>
<div class="metric-label">+18.3% vs last month</div>
</div>
''', unsafe_allow_html=True)HTML Table & Progress Bars
import streamlit as st
import pandas as pd
import numpy as np
st.title('Custom HTML Table')
np.random.seed(42)
data = pd.DataFrame({
'Product': ['Laptop', 'Phone', 'Tablet', 'Watch'],
'Revenue': [125000, 89000, 42000, 31000],
'Progress': [85, 62, 41, 28],
})
def progress_bar(pct, color='#4CAF50'):
return (f'<div style="background:#eee;border-radius:4px;height:8px">' +
f'<div style="width:{pct}%;background:{color};height:8px;border-radius:4px"></div></div>')
html = '<table style="width:100%;border-collapse:collapse">'
html += '<tr><th>Product</th><th>Revenue</th><th>Target %</th></tr>'
for _, row in data.iterrows():
color = '#4CAF50' if row['Progress'] >= 60 else '#FF9800'
html += (f'<tr style="border-bottom:1px solid #eee">'
f'<td>{row["Product"]}</td>'
f'<td>${row["Revenue"]:,}</td>'
f'<td>{progress_bar(row["Progress"], color)}{row["Progress"]}%</td>'
f'</tr>')
html += '</table>'
st.markdown(html, unsafe_allow_html=True)JavaScript Integration with components.html
import streamlit as st
import streamlit.components.v1 as components
st.title('Custom JavaScript Chart')
# Embed Chart.js (CDN) for a custom animated chart
chart_html = '''
<canvas id="myChart" width="600" height="300"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'Revenue ($K)',
data: [120, 190, 170, 220, 250, 280],
backgroundColor: ['#667eea','#764ba2','#f093fb','#f5576c','#4facfe','#43e97b'],
borderRadius: 8,
}]
},
options: { animation: { duration: 1000 }, responsive: false }
});
</script>
'''
components.html(chart_html, height=320)
# Or use Vega-Altair (built into Streamlit)
import altair as alt
import pandas as pd
df = pd.DataFrame({'month': ['Jan','Feb','Mar'], 'revenue': [120, 190, 170]})
st.altair_chart(alt.Chart(df).mark_bar().encode(x='month', y='revenue'), use_container_width=True)Custom Theme via config.toml
# Streamlit theming via .streamlit/config.toml
# This code demonstrates configuration patterns
print('''
# .streamlit/config.toml
[theme]
primaryColor = "#6C63FF" # accent color
backgroundColor = "#0f1117" # main background
secondaryBackgroundColor = "#1c2128" # sidebar/card bg
textColor = "#c9d1d9" # body text
font = "sans serif"
# Dynamic theming in-app
import streamlit as st
theme = st.sidebar.selectbox("Theme", ["Light", "Dark", "Purple"])
themes = {
"Dark": {"bg": "#0f1117", "text": "#c9d1d9", "acc": "#58a6ff"},
"Light": {"bg": "#ffffff", "text": "#1a1a1a", "acc": "#6C63FF"},
"Purple": {"bg": "#1a0030", "text": "#e0d0ff", "acc": "#a371f7"},
}
t = themes[theme]
st.markdown(f"""
<style>
.stApp {{background-color: {t['bg']}; color: {t['text']}}}
h1 {{color: {t['acc']}}}
</style>
""", unsafe_allow_html=True)
''')
import streamlit as st
st.title('Theme Demo')
st.info('Set theme in .streamlit/config.toml or override with st.markdown CSS')
st.markdown('<h3 style="color:#6C63FF">Custom colored heading</h3>', unsafe_allow_html=True)import streamlit as st
import pandas as pd
import numpy as np
st.set_page_config(page_title='Exec Dashboard', layout='wide')
# Inject company brand CSS
st.markdown('''
<style>
body { font-family: 'Segoe UI', sans-serif; }
.kpi-card { background: #1e2d3d; border: 1px solid #2d4a66;
border-radius: 10px; padding: 20px; text-align: center; }
.kpi-val { font-size: 2rem; font-weight: 700; color: #00d4ff; }
.kpi-lbl { font-size: 0.85rem; color: #8899aa; margin-top: 4px; }
</style>
''', unsafe_allow_html=True)
st.title('Executive Dashboard')
kpis = [('Revenue', '$4.2M', '+12.3%'), ('Users', '142K', '+8.1%'), ('NPS', '72', '+5pts')]
cols = st.columns(3)
for col, (label, value, delta) in zip(cols, kpis):
col.markdown(f'''
<div class="kpi-card">
<div class="kpi-val">{value}</div>
<div class="kpi-lbl">{label} <span style="color:#00ff88">{delta}</span></div>
</div>
''', unsafe_allow_html=True)
np.random.seed(42)
df = pd.DataFrame({'month': range(12), 'revenue': np.random.exponential(400, 12).cumsum()})
st.line_chart(df.set_index('month'))import streamlit as st
def make_kpi_card(title: str, value: str, delta: str, color: str = '#00d4ff') -> str:
# TODO: return HTML string for a styled card
# delta is positive if starts with '+', negative if starts with '-'
# use color parameter for value text
pass
st.set_page_config(layout='wide')
# TODO: Inject CSS for card styling and dark/light theme
# TODO: Add a theme selectbox to the sidebar
# TODO: Display 4 KPI cards in a 2x2 grid using st.columns
kpis = [
('Revenue', '$2.1M', '+15.2%'),
('Churn Rate', '3.4%', '-0.8%'),
('Active Users', '89K', '+22.1%'),
('Avg Session', '4m32s', '-12s'),
]
# TODO: render cards
Streamlit makes it easy to build interactive dashboards with sliders, dropdowns, file uploaders, and multi-column layouts. Session state preserves data between user interactions. All Python, no JavaScript needed.
Interactive Data Explorer with Sidebar
# app.py β run with: streamlit run app.py
import streamlit as st
import pandas as pd
import numpy as np
st.title("Interactive Data Explorer")
st.sidebar.header("Settings")
n_rows = st.sidebar.slider("Number of rows", 50, 1000, 200, step=50)
noise = st.sidebar.slider("Noise level", 0.0, 5.0, 1.0, step=0.5)
chart = st.sidebar.selectbox("Chart type", ["Line", "Bar", "Scatter"])
np.random.seed(42)
df = pd.DataFrame({
"date": pd.date_range("2024-01-01", periods=n_rows, freq="D"),
"value": np.cumsum(np.random.normal(0, noise, n_rows)) + 100,
"category": np.random.choice(["A","B","C"], n_rows),
})
st.write(f"Dataset: {len(df)} rows")
col1, col2 = st.columns(2)
col1.metric("Mean", f"{df['value'].mean():.2f}")
col2.metric("Std", f"{df['value'].std():.2f}")
if chart == "Line":
st.line_chart(df.set_index("date")["value"])
elif chart == "Bar":
st.bar_chart(df.groupby("category")["value"].mean())
else:
st.scatter_chart(df.rename(columns={"date":"x","value":"y"}), x="x", y="y")
if st.checkbox("Show raw data"):
st.dataframe(df.head(20), use_container_width=True)
CSV Upload & Analysis App
# Upload and analyze CSV files
import streamlit as st
import pandas as pd
import numpy as np
st.title("CSV Analyzer")
uploaded = st.file_uploader("Upload CSV", type="csv")
if uploaded:
df = pd.read_csv(uploaded)
st.success(f"Loaded {df.shape[0]} rows x {df.shape[1]} columns")
st.dataframe(df.head(10))
numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
if numeric_cols:
col = st.selectbox("Analyze column:", numeric_cols)
c1, c2, c3, c4 = st.columns(4)
c1.metric("Mean", f"{df[col].mean():.3f}")
c2.metric("Median", f"{df[col].median():.3f}")
c3.metric("Std", f"{df[col].std():.3f}")
c4.metric("Nulls", str(df[col].isna().sum()))
st.subheader(f"Distribution of {col}")
hist_data = np.histogram(df[col].dropna(), bins=30)
hist_df = pd.DataFrame({"value": hist_data[1][:-1], "count": hist_data[0]})
st.bar_chart(hist_df.set_index("value"))
else:
st.info("Upload a CSV file to get started")
# Demo data
demo = pd.DataFrame({"x": np.arange(20), "y": np.random.randn(20)})
st.line_chart(demo.set_index("x"))
Multi-Page App with Session State
# Multi-page app with session state
import streamlit as st
import pandas as pd
import numpy as np
if "data" not in st.session_state:
st.session_state.data = None
if "filters" not in st.session_state:
st.session_state.filters = {}
page = st.sidebar.radio("Navigate", ["Generate Data", "Filter & Explore", "Summary"])
if page == "Generate Data":
st.header("Data Generator")
n = st.number_input("Rows", 100, 10000, 500)
seed = st.number_input("Random seed", 0, 999, 42)
if st.button("Generate"):
np.random.seed(seed)
st.session_state.data = pd.DataFrame({
"age": np.random.randint(18, 80, n),
"income": np.random.lognormal(10, 0.5, n),
"score": np.random.beta(2, 5, n),
"segment": np.random.choice(["A","B","C"], n),
})
st.success(f"Generated {n} rows!")
elif page == "Filter & Explore":
if st.session_state.data is None:
st.warning("Generate data first!")
else:
df = st.session_state.data
min_age, max_age = st.slider("Age range", 18, 80, (25, 65))
seg = st.multiselect("Segments", ["A","B","C"], default=["A","B","C"])
filtered = df[(df.age.between(min_age, max_age)) & (df.segment.isin(seg))]
st.write(f"Filtered: {len(filtered)} rows"); st.dataframe(filtered.head(20))
else:
if st.session_state.data is not None:
df = st.session_state.data
st.header("Summary Statistics"); st.dataframe(df.describe())
# Sales Dashboard app
import streamlit as st
import pandas as pd
import numpy as np
st.set_page_config(page_title="Sales Dashboard", layout="wide")
st.title("Sales Performance Dashboard")
# Sidebar filters
st.sidebar.header("Filters")
months = st.sidebar.multiselect("Month", list(range(1,13)), default=list(range(1,13)))
region = st.sidebar.multiselect("Region", ["North","South","East","West"],
default=["North","South","East","West"])
min_rev = st.sidebar.number_input("Min Revenue", 0, 100000, 0, step=1000)
# Simulated sales data
np.random.seed(0)
n = 500
df = pd.DataFrame({
"month": np.random.randint(1, 13, n),
"region": np.random.choice(["North","South","East","West"], n),
"product": np.random.choice(["A","B","C"], n),
"revenue": np.random.lognormal(8, 1, n),
"units": np.random.poisson(50, n),
})
filtered = df[(df.month.isin(months)) & (df.region.isin(region)) & (df.revenue >= min_rev)]
# KPI row
c1, c2, c3, c4 = st.columns(4)
c1.metric("Total Revenue", f"${filtered.revenue.sum():,.0f}")
c2.metric("Avg Revenue", f"${filtered.revenue.mean():.0f}")
c3.metric("Total Units", f"{filtered.units.sum():,}")
c4.metric("Orders", f"{len(filtered):,}")
# Charts
col_a, col_b = st.columns(2)
with col_a:
st.subheader("Revenue by Region")
st.bar_chart(filtered.groupby("region")["revenue"].sum())
with col_b:
st.subheader("Revenue by Product")
st.bar_chart(filtered.groupby("product")["revenue"].sum())
st.subheader("Data Table")
st.dataframe(filtered.head(50), use_container_width=True)
# Build your own dashboard β run with: streamlit run app.py
import streamlit as st
import pandas as pd
import numpy as np
st.title("Customer Analytics Dashboard")
# TODO: Sidebar with sliders for date range, dropdowns for segment/product
# TODO: Generate or upload simulated customer data (age, LTV, segment, country)
# TODO: Show 4 KPI metrics (total customers, avg LTV, retention rate, churn rate)
# TODO: Two-column layout: bar chart by segment, line chart over time
# TODO: Filterable data table with search
# TODO: Add a session state counter for "number of queries run"
Deploy ML models as interactive web apps with real-time predictions, batch file upload, and downloadable results. Use @st.cache_resource to load models once. st.empty enables live streaming updates.
Real-Time Loan Approval Predictor
# ML prediction app with real-time scoring
import streamlit as st
import pandas as pd
import numpy as np
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.preprocessing import StandardScaler
# Train model (in real app, load pre-trained model)
@st.cache_resource
def train_model():
np.random.seed(0)
X = np.random.randn(2000, 5)
y = (X[:,0] + X[:,1] > 0).astype(int)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
model = GradientBoostingClassifier(n_estimators=50, random_state=0)
model.fit(X_scaled, y)
return model, scaler
model, scaler = train_model()
st.title("Loan Approval Predictor")
st.write("Enter applicant details to get an instant decision")
col1, col2 = st.columns(2)
with col1:
income = st.number_input("Annual Income ($)", 20000, 500000, 75000, 5000)
credit = st.slider("Credit Score", 300, 850, 680)
debt = st.slider("Debt-to-Income Ratio", 0.0, 1.0, 0.3, 0.05)
with col2:
tenure = st.number_input("Employment Years", 0, 40, 5)
loan_amt= st.number_input("Loan Amount ($)", 5000, 1000000, 150000, 5000)
if st.button("Predict", type="primary"):
X_input = scaler.transform([[income/100000, credit/1000, debt, tenure/40, loan_amt/1000000]])
prob = model.predict_proba(X_input)[0][1]
st.metric("Approval Probability", f"{prob:.1%}")
if prob > 0.6:
st.success("Decision: APPROVED")
else:
st.error("Decision: DECLINED")
Batch Predictions with CSV Upload & Download
# Batch prediction with file upload + download
import streamlit as st
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
import io
@st.cache_resource
def get_model():
np.random.seed(0)
X = np.random.randn(500, 4); y = (X[:,0]>0).astype(int)
return RandomForestClassifier(n_estimators=50, random_state=0).fit(X, y)
model = get_model()
st.title("Batch ML Predictions")
uploaded = st.file_uploader("Upload data CSV (4 numeric features)", type="csv")
if uploaded:
df = pd.read_csv(uploaded)
numeric_df = df.select_dtypes(include=np.number).iloc[:, :4]
if len(numeric_df.columns) < 4:
st.error("Need at least 4 numeric columns")
else:
df["prediction"] = model.predict(numeric_df.values)
df["probability"] = model.predict_proba(numeric_df.values)[:, 1]
col1, col2 = st.columns(2)
col1.metric("Positive predictions", int(df.prediction.sum()))
col2.metric("Avg probability", f"{df.probability.mean():.3f}")
st.dataframe(df.head(20))
csv_out = df.to_csv(index=False).encode("utf-8")
st.download_button("Download predictions", csv_out, "predictions.csv", "text/csv")
else:
st.info("Upload a CSV to get batch predictions")
Live Data Streaming with st.empty
# Real-time streaming simulation with st.empty
import streamlit as st
import numpy as np
import time
st.title("Live Data Stream Simulator")
placeholder = st.empty()
chart_placeholder = st.empty()
stop = st.button("Stop Streaming")
n_points = st.sidebar.slider("Points to stream", 20, 200, 50)
speed = st.sidebar.slider("Update speed (ms)", 100, 1000, 300)
data_history = []
if not stop:
for i in range(n_points):
new_val = np.sin(i * 0.3) + np.random.normal(0, 0.2)
data_history.append(new_val)
with placeholder.container():
c1, c2, c3 = st.columns(3)
c1.metric("Current", f"{new_val:.3f}")
c2.metric("Mean", f"{np.mean(data_history):.3f}")
c3.metric("Std Dev", f"{np.std(data_history):.3f}")
import pandas as pd
chart_placeholder.line_chart(pd.DataFrame({"signal": data_history}))
time.sleep(speed / 1000)
st.success(f"Streamed {n_points} data points!")
# Churn prediction app with explanations
import streamlit as st
import pandas as pd
import numpy as np
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.preprocessing import StandardScaler
@st.cache_resource
def build_churn_model():
np.random.seed(42)
n = 3000
X = np.column_stack([
np.random.randint(1, 120, n), # tenure_months
np.random.lognormal(7, 0.5, n), # monthly_spend
np.random.randint(1, 10, n), # num_products
np.random.randint(0, 5, n), # support_calls
np.random.randint(0, 2, n), # is_premium
])
prob = 1/(1+np.exp(-(- X[:,0]/60 + X[:,3]/3 - X[:,4]*0.5)))
y = np.random.binomial(1, prob)
sc = StandardScaler(); Xs = sc.fit_transform(X)
model = GradientBoostingClassifier(n_estimators=100, random_state=0).fit(Xs, y)
return model, sc
model, scaler = build_churn_model()
feature_names = ["tenure_months","monthly_spend","num_products","support_calls","is_premium"]
st.title("Customer Churn Predictor")
st.sidebar.header("Customer Profile")
tenure = st.sidebar.slider("Tenure (months)", 1, 120, 24)
spend = st.sidebar.number_input("Monthly Spend ($)", 10, 2000, 150)
products= st.sidebar.slider("Number of Products", 1, 9, 2)
calls = st.sidebar.slider("Support Calls (6m)", 0, 10, 1)
premium = st.sidebar.checkbox("Is Premium Customer", value=False)
X_in = scaler.transform([[tenure, spend, products, calls, int(premium)]])
churn_prob = model.predict_proba(X_in)[0][1]
st.metric("Churn Probability", f"{churn_prob:.1%}",
delta=f"{'HIGH RISK' if churn_prob > 0.5 else 'LOW RISK'}")
st.progress(churn_prob)
# Feature importances
fi = model.feature_importances_
st.subheader("Key Drivers")
fi_df = pd.DataFrame({"Feature":feature_names, "Importance":fi}).sort_values("Importance", ascending=False)
st.bar_chart(fi_df.set_index("Feature"))
# Build an ML demo app β run with: streamlit run app.py
import streamlit as st
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
import io
# TODO: Build and cache a RandomForest model on simulated credit data (5 features)
# TODO: Single prediction form: sliders for each feature, button to predict
# TODO: Show prediction + probability with st.metric and colored st.success/st.error
# TODO: File upload section for batch predictions (predict on all rows)
# TODO: Download button for results CSV
# TODO: Bar chart of feature importances
Combine Plotly charts with Streamlit for pixel-perfect interactive visualizations. Use st.form for atomic multi-input submissions, st.cache_data with TTL for refreshable data sources, and st.tabs for clean multi-view layouts.
Plotly Charts in Tabs
# Plotly + Streamlit: interactive charts
import streamlit as st
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
st.title("Advanced Visualizations")
np.random.seed(0)
n = 200
df = pd.DataFrame({
"x": np.random.randn(n),
"y": np.random.randn(n),
"size": np.abs(np.random.randn(n)) * 10 + 5,
"color": np.random.choice(["A","B","C"], n),
"revenue": np.random.lognormal(8, 1, n),
})
tab1, tab2, tab3 = st.tabs(["Scatter", "Histogram", "3D"])
with tab1:
fig = px.scatter(df, x="x", y="y", color="color", size="size",
hover_data=["revenue"], title="Interactive Scatter")
st.plotly_chart(fig, use_container_width=True)
with tab2:
col = st.selectbox("Column", ["x","y","revenue"])
fig2 = px.histogram(df, x=col, color="color", nbins=30, barmode="overlay")
st.plotly_chart(fig2, use_container_width=True)
with tab3:
fig3 = px.scatter_3d(df, x="x", y="y", z="revenue", color="color")
st.plotly_chart(fig3, use_container_width=True)
Experiment Tracker with st.form
# Advanced state & forms
import streamlit as st
import pandas as pd
import numpy as np
st.title("Experiment Tracker")
if "experiments" not in st.session_state:
st.session_state.experiments = []
if "exp_count" not in st.session_state:
st.session_state.exp_count = 0
with st.form("new_experiment"):
st.subheader("Log New Experiment")
col1, col2 = st.columns(2)
model_name = col1.selectbox("Model", ["Ridge","RF","GBM","XGBoost"])
lr = col2.selectbox("Learning Rate", [0.001, 0.01, 0.1, 0.5])
n_est = st.slider("Estimators", 10, 500, 100, step=10)
notes = st.text_input("Notes", placeholder="Brief description...")
submitted = st.form_submit_button("Run Experiment")
if submitted:
np.random.seed(st.session_state.exp_count)
auc = min(0.99, 0.7 + np.random.exponential(0.08) + n_est/5000)
st.session_state.experiments.append({
"id": st.session_state.exp_count + 1,
"model": model_name, "lr": lr,
"n_est": n_est, "auc": round(auc, 4), "notes": notes
})
st.session_state.exp_count += 1
st.success(f"Experiment logged! AUC={auc:.4f}")
if st.session_state.experiments:
df = pd.DataFrame(st.session_state.experiments)
best = df.loc[df.auc.idxmax()]
st.metric("Best AUC", f"{best.auc:.4f}", f"Model: {best.model}")
st.dataframe(df.sort_values("auc", ascending=False))
System Monitoring with st.cache_data + Alerts
# Streamlit with database-style caching + alerts
import streamlit as st
import pandas as pd
import numpy as np
import time
@st.cache_data(ttl=60)
def load_data(n_rows):
np.random.seed(42)
return pd.DataFrame({
"timestamp":pd.date_range("2024-01-01", periods=n_rows, freq="h"),
"cpu_pct": np.clip(50 + np.cumsum(np.random.normal(0, 2, n_rows)), 5, 99),
"memory": np.clip(40 + np.cumsum(np.random.normal(0, 1, n_rows)), 10, 95),
"requests": np.abs(np.random.poisson(1000, n_rows).astype(float)),
})
st.title("System Monitoring Dashboard")
n = st.sidebar.slider("Hours of history", 24, 720, 168, step=24)
df = load_data(n)
latest = df.iloc[-1]
c1, c2, c3 = st.columns(3)
delta_cpu = latest.cpu_pct - df.cpu_pct.mean()
c1.metric("CPU%", f"{latest.cpu_pct:.1f}%", f"{delta_cpu:+.1f}% vs avg")
c2.metric("Memory%", f"{latest.memory:.1f}%")
c3.metric("Req/h", f"{latest.requests:.0f}")
if latest.cpu_pct > 80:
st.error("HIGH CPU USAGE ALERT!")
elif latest.cpu_pct > 60:
st.warning("CPU usage elevated")
else:
st.success("All systems normal")
st.subheader("CPU & Memory over Time")
st.line_chart(df.set_index("timestamp")[["cpu_pct","memory"]])
# Full-featured analytics platform
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
st.set_page_config(page_title="Analytics Platform", layout="wide", page_icon="analytics")
@st.cache_data
def generate_data(seed=42):
np.random.seed(seed)
n = 1000
return pd.DataFrame({
"date": pd.date_range("2023-01-01", periods=n, freq="D"),
"revenue": np.cumsum(np.random.normal(1000, 200, n)) + 50000,
"users": np.random.poisson(500, n),
"region": np.random.choice(["NA","EU","APAC","LATAM"], n),
"product": np.random.choice(["Basic","Pro","Enterprise"], n),
"churn": np.random.binomial(1, 0.05, n),
})
df = generate_data()
st.sidebar.header("Filters")
regions = st.sidebar.multiselect("Region", df.region.unique().tolist(), default=df.region.unique().tolist())
products = st.sidebar.multiselect("Product", df.product.unique().tolist(), default=df.product.unique().tolist())
date_range = st.sidebar.date_input("Date range",
[df.date.min(), df.date.max()], df.date.min(), df.date.max())
fdf = df[(df.region.isin(regions)) & (df.product.isin(products))]
if len(date_range) == 2:
fdf = fdf[(fdf.date >= pd.Timestamp(date_range[0])) & (fdf.date <= pd.Timestamp(date_range[1]))]
st.title("Business Analytics Platform")
c1,c2,c3,c4 = st.columns(4)
c1.metric("Total Revenue", f"${fdf.revenue.sum():,.0f}")
c2.metric("Total Users", f"{fdf.users.sum():,}")
c3.metric("Avg Daily Users",f"{fdf.users.mean():.0f}")
c4.metric("Churn Events", f"{fdf.churn.sum()}")
tab1, tab2 = st.tabs(["Trends", "Breakdown"])
with tab1:
st.plotly_chart(px.line(fdf, x="date", y="revenue", color="region"), use_container_width=True)
with tab2:
col_a, col_b = st.columns(2)
with col_a:
st.plotly_chart(px.bar(fdf.groupby("product")["revenue"].sum().reset_index(),
x="product", y="revenue"), use_container_width=True)
with col_b:
st.plotly_chart(px.pie(fdf.groupby("region")["users"].sum().reset_index(),
values="users", names="region"), use_container_width=True)
# Advanced Streamlit app β run with: streamlit run app.py
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
st.set_page_config(layout="wide")
# TODO: @st.cache_data function to generate/load 500-row dataset (product, region, date, revenue, rating)
# TODO: Sidebar: multiselect for region, product; date range slider
# TODO: 4-column KPI row (total revenue, avg rating, orders, top product)
# TODO: 3 tabs: Trends (line chart), Map (bar by region), Ratings (histogram)
# TODO: Download button for filtered data as CSV
# TODO: Experiment logger form using st.form + session state (track 5 experiments max)
Streamlit natively supports Plotly, Altair, Matplotlib, and Bokeh charts. st.plotly_chart() and st.altair_chart() render interactive visualizations with hover, zoom, and click events.
Interactive Plotly Charts
# streamlit_plotly_demo.py
# Run: streamlit run streamlit_plotly_demo.py
import streamlit as st
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
st.title("Interactive Plotly Charts in Streamlit")
# Sample data
np.random.seed(42)
n = 200
df = pd.DataFrame({
"x": np.random.randn(n),
"y": np.random.randn(n),
"category": np.random.choice(["A", "B", "C"], n),
"size": np.random.uniform(5, 20, n),
"value": np.random.randn(n) * 100,
})
# Sidebar controls
st.sidebar.header("Chart Controls")
chart_type = st.sidebar.selectbox("Chart Type", ["Scatter", "Bar", "Histogram", "Box"])
color_col = st.sidebar.selectbox("Color By", ["category"])
selected_cats = st.sidebar.multiselect("Categories", ["A", "B", "C"], default=["A", "B", "C"])
filtered_df = df[df["category"].isin(selected_cats)]
if chart_type == "Scatter":
fig = px.scatter(filtered_df, x="x", y="y", color=color_col,
size="size", hover_data=["value"],
title="Scatter Plot")
elif chart_type == "Bar":
bar_data = filtered_df.groupby("category")["value"].mean().reset_index()
fig = px.bar(bar_data, x="category", y="value",
color="category", title="Mean Value by Category")
elif chart_type == "Histogram":
fig = px.histogram(filtered_df, x="value", color=color_col,
nbins=30, title="Value Distribution")
else:
fig = px.box(filtered_df, x="category", y="value",
color=color_col, title="Value Distribution by Category")
st.plotly_chart(fig, use_container_width=True)
st.dataframe(filtered_df.describe(), use_container_width=True)
Altair Charts
# streamlit_altair_demo.py
import streamlit as st
import altair as alt
import pandas as pd
import numpy as np
st.title("Altair Charts in Streamlit")
np.random.seed(42)
n = 300
# Time series data
dates = pd.date_range("2024-01-01", periods=n, freq="D")
df = pd.DataFrame({
"date": dates,
"value_A": np.cumsum(np.random.randn(n)) + 50,
"value_B": np.cumsum(np.random.randn(n)) + 45,
})
df_melt = df.melt(id_vars="date", value_vars=["value_A", "value_B"],
var_name="series", value_name="value")
# Altair line chart with selection
selection = alt.selection_point(fields=["series"], bind="legend")
chart = (alt.Chart(df_melt)
.mark_line()
.encode(
x=alt.X("date:T", title="Date"),
y=alt.Y("value:Q", title="Value"),
color=alt.Color("series:N"),
opacity=alt.condition(selection, alt.value(1), alt.value(0.2)),
)
.add_params(selection)
.properties(width="container", height=300, title="Interactive Time Series")
)
st.altair_chart(chart, use_container_width=True)
# Heatmap
heatmap_data = pd.DataFrame(
np.random.randn(10, 10),
columns=[f"F{i}" for i in range(10)]
).reset_index().melt(id_vars="index", var_name="feature", value_name="corr")
heatmap = (alt.Chart(heatmap_data)
.mark_rect()
.encode(
x="feature:N",
y=alt.Y("index:O"),
color=alt.Color("corr:Q", scale=alt.Scale(scheme="redblue")),
tooltip=["feature", "index", "corr"],
)
.properties(height=200, title="Feature Correlation Heatmap")
)
st.altair_chart(heatmap, use_container_width=True)
Combined Dashboard Layout
# streamlit_dashboard_demo.py
import streamlit as st
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
st.set_page_config(layout="wide", page_title="Sales Dashboard")
st.title("Sales Performance Dashboard")
np.random.seed(42)
n = 500
df = pd.DataFrame({
"date": pd.date_range("2024-01-01", periods=n, freq="D")[:n % 365 + 1].tolist() * (n // 365 + 1),
"product": np.random.choice(["Widget", "Gadget", "Doohickey"], n),
"region": np.random.choice(["North", "South", "East", "West"], n),
"revenue": np.random.exponential(500, n).round(2),
"units": np.random.randint(1, 50, n),
})
df["date"] = pd.date_range("2024-01-01", periods=n, freq="D")[:n]
# KPI row
col1, col2, col3, col4 = st.columns(4)
col1.metric("Total Revenue", f"${df['revenue'].sum():,.0f}", "+12%")
col2.metric("Total Units", f"{df['units'].sum():,}", "+8%")
col3.metric("Avg Order Value", f"${df['revenue'].mean():.2f}", "+4%")
col4.metric("Active Products", "3", "0")
st.divider()
# Two-column chart layout
col_left, col_right = st.columns(2)
with col_left:
monthly = df.groupby(df["date"].dt.month)["revenue"].sum().reset_index()
monthly.columns = ["month", "revenue"]
fig1 = px.bar(monthly, x="month", y="revenue", title="Monthly Revenue")
st.plotly_chart(fig1, use_container_width=True)
with col_right:
by_region = df.groupby("region")["revenue"].sum().reset_index()
fig2 = px.pie(by_region, names="region", values="revenue", title="Revenue by Region")
st.plotly_chart(fig2, use_container_width=True)
# Data table with search
st.subheader("Raw Data")
search = st.text_input("Search product:", "")
filtered = df[df["product"].str.contains(search, case=False)] if search else df
st.dataframe(filtered.head(20), use_container_width=True)
# streamlit_sales_rw.py
import streamlit as st
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
from io import BytesIO
st.set_page_config(layout="wide", page_title="Sales Analytics")
st.title("Sales Analytics Dashboard")
# Generate sample data
np.random.seed(42)
n = 365
df = pd.DataFrame({
"date": pd.date_range("2024-01-01", periods=n),
"product": np.random.choice(["Product A", "Product B", "Product C"], n),
"region": np.random.choice(["North", "South", "East", "West"], n),
"revenue": np.random.exponential(500, n).round(2),
"units": np.random.randint(1, 100, n),
})
# Sidebar filters
st.sidebar.header("Filters")
date_range = st.sidebar.date_input("Date Range",
value=(df["date"].min(), df["date"].max()),
min_value=df["date"].min(), max_value=df["date"].max())
selected_products = st.sidebar.multiselect("Products", df["product"].unique(), default=df["product"].unique())
# Filter
mask = ((df["date"] >= pd.Timestamp(date_range[0])) &
(df["date"] <= pd.Timestamp(date_range[1])) &
(df["product"].isin(selected_products)))
filtered = df[mask]
# KPIs
c1, c2, c3 = st.columns(3)
c1.metric("Total Revenue", f"${filtered['revenue'].sum():,.0f}")
c2.metric("Total Units", f"{filtered['units'].sum():,}")
c3.metric("Orders", f"{len(filtered):,}")
# Line chart with MA
daily = filtered.groupby("date")["revenue"].sum().reset_index()
daily["MA30"] = daily["revenue"].rolling(30).mean()
fig = go.Figure()
fig.add_trace(go.Scatter(x=daily["date"], y=daily["revenue"], name="Daily Revenue", opacity=0.5))
fig.add_trace(go.Scatter(x=daily["date"], y=daily["MA30"], name="30-day MA", line=dict(width=3)))
fig.update_layout(title="Daily Revenue with Moving Average")
st.plotly_chart(fig, use_container_width=True)
# Download
csv_buffer = filtered.to_csv(index=False).encode()
st.download_button("Download Data", csv_buffer, "sales_data.csv", "text/csv")
# my_chart_app.py
import streamlit as st
import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
"category": np.random.choice(["A","B","C"], 200),
"x": np.random.randn(200),
"y": np.random.randn(200),
"value": np.random.randn(200) * 100,
})
st.title("My Chart App")
# 1. Add selectbox for chart type
# 2. Add multiselect for categories
# 3. Filter df by selected categories
# 4. Create plotly figure based on chart type
# 5. st.plotly_chart(fig, use_container_width=True)
Streamlit reruns the script on every interaction. Session state (st.session_state) persists data across reruns, enabling counters, multi-step forms, authentication, and conversation history.
Counter & Persistent Data
# streamlit_session_state.py
import streamlit as st
import pandas as pd
import numpy as np
st.title("Session State Examples")
# Example 1: Counter
st.header("Counter (persists across reruns)")
if "count" not in st.session_state:
st.session_state.count = 0
if "history" not in st.session_state:
st.session_state.history = []
col1, col2, col3 = st.columns(3)
if col1.button("Increment"):
st.session_state.count += 1
st.session_state.history.append(st.session_state.count)
if col2.button("Decrement"):
st.session_state.count -= 1
st.session_state.history.append(st.session_state.count)
if col3.button("Reset"):
st.session_state.count = 0
st.session_state.history = []
st.metric("Current Count", st.session_state.count)
if st.session_state.history:
st.line_chart(pd.DataFrame({"count": st.session_state.history}))
# Example 2: Shopping cart
st.header("Shopping Cart")
PRODUCTS = {"Widget": 9.99, "Gadget": 24.99, "Doohickey": 4.99}
if "cart" not in st.session_state:
st.session_state.cart = {}
col_a, col_b = st.columns(2)
with col_a:
product = st.selectbox("Add to cart:", list(PRODUCTS.keys()))
qty = st.number_input("Quantity:", min_value=1, max_value=10, value=1)
if st.button("Add"):
st.session_state.cart[product] = st.session_state.cart.get(product, 0) + qty
st.success(f"Added {qty}x {product}")
with col_b:
if st.session_state.cart:
st.write("Cart:")
total = 0
for item, qty in st.session_state.cart.items():
cost = PRODUCTS[item] * qty
total += cost
st.write(f" - {item} x{qty}: ${cost:.2f}")
st.write(f"**Total: ${total:.2f}**")
if st.button("Clear Cart"):
st.session_state.cart = {}
else:
st.info("Cart is empty")
Multi-Step Wizard
# streamlit_wizard.py
import streamlit as st
st.title("Multi-Step Form Wizard")
# Initialize wizard state
if "step" not in st.session_state:
st.session_state.step = 1
if "data" not in st.session_state:
st.session_state.data = {}
# Progress bar
progress = (st.session_state.step - 1) / 3
st.progress(progress, text=f"Step {st.session_state.step} of 3")
# Step 1: Personal Info
if st.session_state.step == 1:
st.subheader("Step 1: Personal Information")
name = st.text_input("Full Name", value=st.session_state.data.get("name", ""))
email = st.text_input("Email", value=st.session_state.data.get("email", ""))
age = st.number_input("Age", min_value=18, max_value=120,
value=st.session_state.data.get("age", 25))
if st.button("Next"):
if name and email:
st.session_state.data.update({"name": name, "email": email, "age": age})
st.session_state.step = 2
st.rerun()
else:
st.error("Please fill all required fields")
# Step 2: Preferences
elif st.session_state.step == 2:
st.subheader("Step 2: Preferences")
interests = st.multiselect("Interests",
["Python", "Data Science", "Machine Learning", "Web Dev", "Cloud"],
default=st.session_state.data.get("interests", []))
experience = st.select_slider("Experience Level",
options=["Beginner", "Intermediate", "Advanced", "Expert"],
value=st.session_state.data.get("experience", "Intermediate"))
col1, col2 = st.columns(2)
if col1.button("Back"):
st.session_state.step = 1; st.rerun()
if col2.button("Next"):
st.session_state.data.update({"interests": interests, "experience": experience})
st.session_state.step = 3; st.rerun()
# Step 3: Review & Submit
elif st.session_state.step == 3:
st.subheader("Step 3: Review & Submit")
st.write("Summary:")
for key, val in st.session_state.data.items():
st.write(f" **{key.title()}:** {val}")
col1, col2 = st.columns(2)
if col1.button("Back"): st.session_state.step = 2; st.rerun()
if col2.button("Submit"):
st.success("Registration complete!")
st.balloons()
st.session_state.step = 1; st.session_state.data = {}
Chatbot with Conversation History
# streamlit_chatbot.py
import streamlit as st
st.title("Chatbot with Conversation History")
st.caption("Session state preserves chat history across reruns")
# Initialize conversation
if "messages" not in st.session_state:
st.session_state.messages = [
{"role": "assistant", "content": "Hello! How can I help you today?"}
]
if "user_count" not in st.session_state:
st.session_state.user_count = 0
# Display chat history
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.write(msg["content"])
# Chat input
if prompt := st.chat_input("Type a message..."):
# Add user message
st.session_state.messages.append({"role": "user", "content": prompt})
st.session_state.user_count += 1
# Generate response (mock - replace with real LLM call)
response = f"You said: '{prompt}' (message #{st.session_state.user_count})"
# Keyword-based mock responses
prompt_lower = prompt.lower()
if "hello" in prompt_lower or "hi" in prompt_lower:
response = "Hi there! Great to meet you!"
elif "help" in prompt_lower:
response = "I can help with data science, Python, and ML questions!"
elif "python" in prompt_lower:
response = "Python is great for data science! Try libraries like pandas, sklearn, and torch."
elif "reset" in prompt_lower:
st.session_state.messages = []
st.session_state.user_count = 0
st.rerun()
st.session_state.messages.append({"role": "assistant", "content": response})
st.rerun()
# Sidebar stats
with st.sidebar:
st.metric("Messages", len(st.session_state.messages))
st.metric("User messages", st.session_state.user_count)
if st.button("Clear Chat"):
st.session_state.messages = [{"role": "assistant", "content": "Chat cleared. How can I help?"}]
st.session_state.user_count = 0
st.rerun()
# streamlit_wizard_rw.py
import streamlit as st
import pandas as pd
import plotly.express as px
from io import StringIO
st.title("Data Analysis Wizard")
if "step" not in st.session_state: st.session_state.step = 1
if "df" not in st.session_state: st.session_state.df = None
if "config" not in st.session_state: st.session_state.config = {}
st.progress((st.session_state.step - 1) / 2, text=f"Step {st.session_state.step}/3")
if st.session_state.step == 1:
st.subheader("Step 1: Upload Data")
uploaded = st.file_uploader("Upload CSV", type=["csv"])
if uploaded:
st.session_state.df = pd.read_csv(uploaded)
st.write(f"Loaded {len(st.session_state.df)} rows, {len(st.session_state.df.columns)} columns")
st.dataframe(st.session_state.df.head(3))
else:
# Demo data
import numpy as np
np.random.seed(42)
st.session_state.df = pd.DataFrame({
"category": np.random.choice(["A","B","C"], 100),
"x": np.random.randn(100), "y": np.random.randn(100),
"value": np.random.exponential(50, 100).round(2),
})
st.info("Using demo data (upload CSV to use your own data)")
if st.button("Next -> Configure"):
st.session_state.step = 2; st.rerun()
elif st.session_state.step == 2:
df = st.session_state.df
st.subheader("Step 2: Configure Chart")
chart_type = st.selectbox("Chart Type", ["scatter", "bar", "histogram", "box"])
cols = df.columns.tolist()
x_col = st.selectbox("X Axis", cols)
y_col = st.selectbox("Y Axis", [c for c in cols if c != x_col])
color_col = st.selectbox("Color By (optional)", ["None"] + cols)
color_col = None if color_col == "None" else color_col
st.session_state.config = {"chart_type": chart_type, "x": x_col, "y": y_col, "color": color_col}
col1, col2 = st.columns(2)
if col1.button("Back"): st.session_state.step = 1; st.rerun()
if col2.button("Next -> View Chart"): st.session_state.step = 3; st.rerun()
elif st.session_state.step == 3:
df = st.session_state.df; cfg = st.session_state.config
st.subheader("Step 3: Analysis Results")
px_fns = {"scatter": px.scatter, "bar": px.bar, "histogram": px.histogram, "box": px.box}
fig = px_fns[cfg["chart_type"]](df, x=cfg["x"], y=cfg["y"], color=cfg["color"])
st.plotly_chart(fig, use_container_width=True)
csv = df.to_csv(index=False).encode()
st.download_button("Download Data", csv, "results.csv", "text/csv")
if st.button("Start Over"): st.session_state.step = 1; st.session_state.config = {}; st.rerun()
# session_counter.py
import streamlit as st
import pandas as pd
st.title("Session State Counter")
# 1. Initialize session_state.count = 0 and session_state.history = []
# 2. Add three columns with Increment, Decrement, Reset buttons
# 3. Update count and append to history on each button click
# 4. Show st.metric for current count
# 5. Show st.line_chart of history if it exists
Streamlit reruns the entire script on every interaction. @st.cache_data caches function outputs by input arguments. @st.cache_resource caches singleton objects like ML models and DB connections.
@st.cache_data for Data Loading
# streamlit_caching.py
import streamlit as st
import pandas as pd
import numpy as np
import time
st.title("Caching Demo")
# @st.cache_data: caches DataFrames, arrays, and serializable objects
@st.cache_data
def load_data(n_rows=1000):
st.write("(Loading data... this message only appears once)")
time.sleep(0.5) # simulate slow loading
np.random.seed(42)
return pd.DataFrame({
"id": range(n_rows),
"category": np.random.choice(list("ABCDE"), n_rows),
"value": np.random.randn(n_rows) * 100,
"date": pd.date_range("2024-01-01", periods=n_rows, freq="h"),
})
@st.cache_data(ttl=300) # expire after 5 minutes
def expensive_computation(df, agg_col):
time.sleep(0.3) # simulate expensive computation
return df.groupby(agg_col)["value"].agg(["mean", "std", "sum", "count"])
# Load data (cached after first load)
n = st.slider("Number of rows", 100, 5000, 1000)
t0 = time.time()
df = load_data(n)
t1 = time.time()
st.success(f"Data loaded in {(t1-t0)*1000:.1f}ms (cached after first load)")
st.write(f"Shape: {df.shape}")
# Computation (cached per column selection)
agg_col = st.selectbox("Group by", ["category"])
t0 = time.time()
result = expensive_computation(df, agg_col)
t1 = time.time()
st.write(f"Computation time: {(t1-t0)*1000:.1f}ms")
st.dataframe(result)
# Cache info
if st.button("Clear Cache"):
st.cache_data.clear()
st.success("Cache cleared!")
@st.cache_resource for Models & Connections
# streamlit_cache_resource.py
import streamlit as st
import pickle
import numpy as np
import time
st.title("Cache Resource: ML Models & Connections")
# @st.cache_resource: for singletons (models, DB connections)
# NOT re-created on every rerun - shared across all sessions
@st.cache_resource
def load_ml_model():
# Simulate loading a large ML model
st.write("(Loading model... only once per session)")
time.sleep(1.0)
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
X, y = load_iris(return_X_y=True)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X, y)
return model
@st.cache_resource
def get_feature_names():
from sklearn.datasets import load_iris
return load_iris().feature_names
# Load model (shared across all users)
st.info("The model loads once and is shared across all users")
t0 = time.time()
model = load_ml_model()
feature_names = get_feature_names()
t1 = time.time()
st.success(f"Model loaded in {(t1-t0)*1000:.1f}ms (instant if cached)")
# Prediction interface
st.subheader("Iris Classifier")
col_inputs = st.columns(4)
values = []
for i, (col, fname) in enumerate(zip(col_inputs, feature_names)):
v = col.number_input(fname, min_value=0.0, max_value=10.0,
value=[5.1, 3.5, 1.4, 0.2][i], step=0.1)
values.append(v)
if st.button("Predict"):
pred = model.predict([values])[0]
prob = model.predict_proba([values])[0]
classes = ["setosa", "versicolor", "virginica"]
st.write(f"Prediction: **{classes[pred]}** ({prob[pred]*100:.1f}% confidence)")
st.bar_chart({classes[i]: float(p) for i, p in enumerate(prob)})
Performance Tips & Lazy Loading
# streamlit_performance_tips.py
import streamlit as st
import pandas as pd
import numpy as np
import time
st.title("Performance Best Practices")
st.header("1. Use st.empty() and placeholders for updates")
placeholder = st.empty()
if st.button("Simulate Real-time Updates"):
for i in range(5):
with placeholder.container():
st.metric("Progress", f"{i*20}%")
st.progress(i / 4)
time.sleep(0.3)
with placeholder.container():
st.success("Done!")
st.header("2. Fragment for partial reruns (Streamlit 1.33+)")
st.code('''
@st.fragment
def update_chart():
# Only this fragment reruns when its widgets change
data = st.slider("Data points", 10, 100, 50)
chart_data = pd.DataFrame(np.random.randn(data, 2), columns=["A", "B"])
st.line_chart(chart_data)
update_chart()
''')
st.header("3. Batch state updates with callbacks")
def increment():
st.session_state.fast_count = st.session_state.get("fast_count", 0) + 1
if "fast_count" not in st.session_state:
st.session_state.fast_count = 0
st.button("Fast Increment", on_click=increment)
st.write(f"Count: {st.session_state.fast_count}")
st.header("4. Performance tips")
tips = [
"Use @st.cache_data for data loading (TTL to auto-refresh)",
"Use @st.cache_resource for ML models, DB connections",
"Avoid heavy computation in the main script body",
"Use st.fragment for partial reruns (avoids full re-render)",
"Use on_click callbacks instead of manual state checks",
"Limit st.dataframe to <10K rows for performance",
]
for tip in tips:
st.write(f"- {tip}")
# streamlit_caching_rw.py
import streamlit as st
import pandas as pd
import numpy as np
import time
st.title("Optimized Dashboard with Caching")
@st.cache_data(ttl=600) # 10-minute TTL
def load_large_dataset(seed=42):
np.random.seed(seed)
n = 50_000 # simulate large dataset
time.sleep(0.2) # simulate I/O delay
return pd.DataFrame({
"date": pd.date_range("2023-01-01", periods=n, freq="h"),
"product": np.random.choice(["A","B","C","D","E"], n),
"region": np.random.choice(["North","South","East","West"], n),
"revenue": np.random.exponential(100, n).round(2),
"units": np.random.randint(1, 50, n),
})
@st.cache_resource
def load_model():
from sklearn.ensemble import RandomForestRegressor
time.sleep(0.5) # simulate model loading
np.random.seed(42)
X = np.random.randn(1000, 5)
y = X.sum(axis=1) + np.random.randn(1000) * 0.1
return RandomForestRegressor(n_estimators=50, random_state=42).fit(X, y)
# Timing
t0 = time.time()
df = load_large_dataset()
load_time = (time.time()-t0)*1000
t0 = time.time()
model = load_model()
model_time = (time.time()-t0)*1000
st.success(f"Data loaded: {load_time:.1f}ms | Model loaded: {model_time:.1f}ms")
st.info("Reload the page - both should be ~0ms on second load (cached!)")
# Dashboard
c1, c2, c3 = st.columns(3)
c1.metric("Total Revenue", f"${df['revenue'].sum():,.0f}")
c2.metric("Total Units", f"{df['units'].sum():,}")
c3.metric("Data Points", f"{len(df):,}")
product = st.selectbox("Product", df["product"].unique())
filtered = df[df["product"] == product]
st.bar_chart(filtered.groupby(filtered["date"].dt.month)["revenue"].sum())
# Prediction
features = np.random.randn(1, 5)
pred = model.predict(features)[0]
st.metric("Predicted Revenue (Random)", f"${pred:.2f}")
# cache_demo.py
import streamlit as st
import pandas as pd
import numpy as np
import time
# 1. Define load_data() with @st.cache_data
# - time.sleep(1) to simulate slow loading
# - Returns pd.DataFrame with 10K rows
# 2. Call load_data() and time it with time.time()
# 3. Show st.metric("Load time", f"{elapsed:.0f}ms")
# 4. Note: first call is slow, subsequent calls are ~0ms
st.form() groups widgets and defers Streamlit reruns until the form is submitted. This prevents partial state updates and improves UX for multi-field inputs like search or settings panels.
st.form() Basics
# streamlit_forms.py
import streamlit as st
import pandas as pd
import numpy as np
st.title("Forms & Input Validation")
# Without form: every widget change triggers a rerun
st.subheader("Without Form (instant reruns)")
name_no_form = st.text_input("Name (no form)", "Alice")
age_no_form = st.number_input("Age (no form)", 18, 120, 25)
st.write(f"Name: {name_no_form}, Age: {age_no_form}")
st.divider()
# With form: batch inputs, rerun only on submit
st.subheader("With Form (batched submit)")
with st.form("user_profile_form"):
st.write("Fill in your profile:")
col1, col2 = st.columns(2)
with col1:
name = st.text_input("Full Name", "Alice Johnson")
email = st.text_input("Email", "alice@example.com")
age = st.number_input("Age", min_value=18, max_value=120, value=30)
with col2:
role = st.selectbox("Role", ["Data Scientist", "ML Engineer", "Analyst", "Manager"])
experience = st.slider("Years of Experience", 0, 30, 5)
skills = st.multiselect("Skills", ["Python", "SQL", "R", "ML", "DL", "Cloud"])
newsletter = st.checkbox("Subscribe to newsletter", value=True)
submitted = st.form_submit_button("Save Profile", use_container_width=True)
if submitted:
profile = {
"name": name, "email": email, "age": age,
"role": role, "experience": experience,
"skills": skills, "newsletter": newsletter
}
st.success("Profile saved!")
st.json(profile)
Input Validation with Forms
# streamlit_validation.py
import streamlit as st
import re
import pandas as pd
import numpy as np
st.title("Form Input Validation")
# Validation functions
def validate_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
def validate_phone(phone):
digits = re.sub(r'[^\d]', '', phone)
return len(digits) == 10
def validate_age(age):
return 18 <= age <= 120
with st.form("validated_form"):
st.subheader("Registration Form")
name = st.text_input("Full Name *")
email = st.text_input("Email *")
phone = st.text_input("Phone (10 digits)")
age = st.number_input("Age *", min_value=0, max_value=150, value=25)
password = st.text_input("Password *", type="password")
confirm_pw = st.text_input("Confirm Password *", type="password")
submitted = st.form_submit_button("Register")
if submitted:
errors = []
if not name.strip():
errors.append("Full name is required")
if not validate_email(email):
errors.append(f"Invalid email: {email}")
if phone and not validate_phone(phone):
errors.append(f"Invalid phone number (need 10 digits)")
if not validate_age(age):
errors.append(f"Age must be between 18 and 120")
if len(password) < 8:
errors.append("Password must be at least 8 characters")
if password != confirm_pw:
errors.append("Passwords do not match")
if errors:
for err in errors:
st.error(err)
else:
st.success(f"Welcome, {name}! Registration successful.")
st.balloons()
Search & Filter Form
# streamlit_search_form.py
import streamlit as st
import pandas as pd
import numpy as np
st.title("Search & Filter Interface")
np.random.seed(42)
n = 1000
df = pd.DataFrame({
"product_id": range(1, n+1),
"name": [f"Product_{i}" for i in range(1, n+1)],
"category": np.random.choice(["Electronics","Clothing","Books","Sports","Food"], n),
"price": np.random.exponential(50, n).round(2),
"rating": np.random.uniform(1, 5, n).round(1),
"in_stock": np.random.choice([True, False], n, p=[0.8, 0.2]),
})
# Search form (no reruns until Search is clicked)
with st.form("search_form"):
st.subheader("Filter Products")
col1, col2, col3 = st.columns(3)
with col1:
search_text = st.text_input("Search by name")
categories = st.multiselect("Category",
df["category"].unique().tolist(),
default=df["category"].unique().tolist())
with col2:
min_price, max_price = st.slider("Price Range ($)",
0.0, float(df["price"].max()), (0.0, 100.0))
min_rating = st.slider("Minimum Rating", 1.0, 5.0, 3.0, 0.5)
with col3:
in_stock_only = st.checkbox("In Stock Only", value=False)
sort_by = st.selectbox("Sort By", ["rating", "price", "name"])
sort_desc = st.checkbox("Sort Descending", value=True)
searched = st.form_submit_button("Search", use_container_width=True)
if searched or True: # show all initially
filtered = df[
(df["category"].isin(categories)) &
(df["price"] >= min_price) & (df["price"] <= max_price) &
(df["rating"] >= min_rating)
]
if search_text:
filtered = filtered[filtered["name"].str.contains(search_text, case=False)]
if in_stock_only:
filtered = filtered[filtered["in_stock"]]
filtered = filtered.sort_values(sort_by, ascending=not sort_desc)
st.write(f"Found {len(filtered)} products")
st.dataframe(filtered.head(20), use_container_width=True)
# streamlit_analysis_form.py
import streamlit as st
import pandas as pd
import plotly.express as px
import numpy as np
st.title("Dataset Analysis Form")
# Generate sample or upload
uploaded = st.file_uploader("Upload CSV (optional)", type=["csv"])
if uploaded:
df = pd.read_csv(uploaded)
else:
np.random.seed(42)
df = pd.DataFrame({
"date": pd.date_range("2024-01-01", periods=200, freq="D"),
"region": np.random.choice(["North","South","East","West"], 200),
"sales": np.random.exponential(500, 200).round(2),
"units": np.random.randint(1, 100, 200),
"cost": np.random.exponential(200, 200).round(2),
})
st.info("Using demo data")
st.write(f"Dataset: {df.shape[0]} rows, {df.shape[1]} columns")
with st.form("analysis_form"):
st.subheader("Configure Analysis")
numeric_cols = df.select_dtypes("number").columns.tolist()
cat_cols = df.select_dtypes("object").columns.tolist()
x_col = st.selectbox("X Column", df.columns.tolist())
y_col = st.selectbox("Y Column", numeric_cols)
group_col = st.selectbox("Group By", ["(none)"] + cat_cols)
chart_type = st.radio("Chart Type", ["bar", "scatter", "line"], horizontal=True)
submitted = st.form_submit_button("Analyze")
if submitted:
errors = []
if y_col not in numeric_cols:
errors.append(f"{y_col} must be a numeric column")
if x_col == y_col:
errors.append("X and Y columns must be different")
if errors:
for e in errors: st.error(e)
else:
group = None if group_col == "(none)" else group_col
if chart_type == "bar":
fig = px.bar(df, x=x_col, y=y_col, color=group, title=f"{y_col} by {x_col}")
elif chart_type == "scatter":
fig = px.scatter(df, x=x_col, y=y_col, color=group, title=f"{y_col} vs {x_col}")
else:
fig = px.line(df, x=x_col, y=y_col, color=group, title=f"{y_col} over {x_col}")
st.plotly_chart(fig, use_container_width=True)
# validated_form.py
import streamlit as st
import re
st.title("Registration Form")
with st.form("register"):
name = st.text_input("Name")
email = st.text_input("Email")
age = st.number_input("Age", 0, 150, 25)
submitted = st.form_submit_button("Submit")
if submitted:
errors = []
# 1. Validate name is not empty
# 2. Validate email with regex r'^[\w.]+@[\w]+\.[a-z]{2,}$'
# 3. Validate age is 18-120
# 4. Show errors or st.success("Welcome!")
st.file_uploader() accepts CSV, Excel, images, and arbitrary files. st.download_button() provides one-click download of DataFrames, plots, or any binary content.
File Upload & CSV Processing
# streamlit_file_upload.py
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
from io import BytesIO, StringIO
st.title("File Upload & Processing")
# Upload CSV
st.header("CSV Upload & Analysis")
uploaded_csv = st.file_uploader("Upload a CSV file", type=["csv", "xlsx"])
if uploaded_csv is not None:
# Read file
if uploaded_csv.name.endswith(".xlsx"):
df = pd.read_excel(uploaded_csv)
else:
df = pd.read_csv(uploaded_csv)
st.success(f"Loaded: {len(df)} rows x {len(df.columns)} columns")
col1, col2 = st.columns(2)
col1.metric("Rows", len(df))
col2.metric("Columns", len(df.columns))
st.subheader("Preview")
st.dataframe(df.head(10), use_container_width=True)
st.subheader("Data Types")
st.dataframe(pd.DataFrame({"dtype": df.dtypes, "non_null": df.count(),
"null_count": df.isnull().sum()}))
st.subheader("Statistics")
st.dataframe(df.describe(), use_container_width=True)
# Auto chart for numeric columns
numeric_cols = df.select_dtypes("number").columns.tolist()
if numeric_cols:
col = st.selectbox("Histogram of:", numeric_cols)
fig = px.histogram(df, x=col, title=f"Distribution of {col}")
st.plotly_chart(fig, use_container_width=True)
else:
st.info("Upload a CSV file to get started")
# Show demo
with st.expander("Try with demo data"):
np.random.seed(42)
demo_df = pd.DataFrame({
"name": [f"Item_{i}" for i in range(20)],
"value": np.random.randn(20) * 100,
"category": np.random.choice(["A","B","C"], 20),
})
st.dataframe(demo_df)
Download Buttons & Export
# streamlit_download.py
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
from io import BytesIO
st.title("Data Download Examples")
np.random.seed(42)
df = pd.DataFrame({
"product": [f"Item_{i}" for i in range(50)],
"category": np.random.choice(["A","B","C"], 50),
"sales": np.random.exponential(1000, 50).round(2),
"units": np.random.randint(10, 500, 50),
"rating": np.random.uniform(1, 5, 50).round(1),
})
st.dataframe(df.head(10), use_container_width=True)
st.subheader("Download Options")
col1, col2, col3 = st.columns(3)
# 1. Download as CSV
csv_data = df.to_csv(index=False).encode("utf-8")
col1.download_button(
label="Download CSV",
data=csv_data,
file_name="sales_data.csv",
mime="text/csv",
use_container_width=True,
)
# 2. Download as Excel
@st.cache_data
def to_excel(df):
output = BytesIO()
with pd.ExcelWriter(output, engine="openpyxl") as writer:
df.to_excel(writer, index=False, sheet_name="Data")
summary = df.groupby("category")["sales"].sum().reset_index()
summary.to_excel(writer, index=False, sheet_name="Summary")
return output.getvalue()
excel_data = to_excel(df)
col2.download_button(
label="Download Excel",
data=excel_data,
file_name="sales_report.xlsx",
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
use_container_width=True,
)
# 3. Download chart as HTML
fig = px.bar(df.groupby("category")["sales"].sum().reset_index(),
x="category", y="sales", title="Sales by Category")
st.plotly_chart(fig, use_container_width=True)
chart_html = fig.to_html()
col3.download_button(
label="Download Chart",
data=chart_html.encode("utf-8"),
file_name="sales_chart.html",
mime="text/html",
use_container_width=True,
)
Image Upload & Processing
# streamlit_image_upload.py
import streamlit as st
import numpy as np
from io import BytesIO
st.title("Image Upload & Processing")
uploaded_img = st.file_uploader("Upload an Image", type=["jpg", "jpeg", "png"])
if uploaded_img is not None:
# Display original
col1, col2 = st.columns(2)
with col1:
st.image(uploaded_img, caption="Original", use_container_width=True)
try:
from PIL import Image, ImageFilter, ImageEnhance
img = Image.open(uploaded_img)
st.write(f"Size: {img.size}, Mode: {img.mode}")
# Processing options
effect = st.selectbox("Effect", ["None", "Grayscale", "Blur", "Sharpen", "Rotate"])
if effect == "Grayscale":
processed = img.convert("L")
elif effect == "Blur":
radius = st.slider("Blur Radius", 1, 10, 3)
processed = img.filter(ImageFilter.GaussianBlur(radius))
elif effect == "Sharpen":
processed = img.filter(ImageFilter.SHARPEN)
elif effect == "Rotate":
angle = st.slider("Angle", 0, 360, 90)
processed = img.rotate(angle)
else:
processed = img
with col2:
st.image(processed, caption=f"Processed ({effect})", use_container_width=True)
# Download processed image
buf = BytesIO()
processed.save(buf, format="PNG")
st.download_button("Download Processed Image", buf.getvalue(),
"processed.png", "image/png")
except ImportError:
st.warning("Install Pillow for image processing: pip install Pillow")
else:
st.info("Upload an image to process it")
st.write("Supported effects: Grayscale, Blur, Sharpen, Rotate")
# streamlit_data_cleaner.py
import streamlit as st
import pandas as pd
import numpy as np
from io import BytesIO
st.title("Data Cleaning Tool")
@st.cache_data
def generate_messy_data():
np.random.seed(42)
n = 1000
df = pd.DataFrame({
"id": range(n),
"name": [f"Item_{i}" if np.random.random() > 0.03 else None for i in range(n)],
"price": np.where(np.random.random(n) < 0.05, None,
np.where(np.random.random(n) < 0.02, -99.99,
np.random.exponential(50, n).round(2))),
"category": np.random.choice(["A","B","C",None], n, p=[0.35,0.35,0.25,0.05]),
"quantity": np.random.randint(-5, 100, n), # some negatives
})
# Add duplicates
df = pd.concat([df, df.sample(50, random_state=42)]).reset_index(drop=True)
return df
uploaded = st.file_uploader("Upload CSV", type="csv")
df = pd.read_csv(uploaded) if uploaded else generate_messy_data()
st.subheader("Data Quality Report")
col1, col2, col3 = st.columns(3)
col1.metric("Total Rows", len(df))
col2.metric("Null Values", df.isnull().sum().sum())
col3.metric("Duplicates", df.duplicated().sum())
st.dataframe(pd.DataFrame({
"nulls": df.isnull().sum(),
"null_pct": (df.isnull().mean()*100).round(1),
"dtype": df.dtypes,
}), use_container_width=True)
st.subheader("Cleaning Options")
col_a, col_b = st.columns(2)
drop_nulls = col_a.checkbox("Drop rows with nulls", value=True)
drop_dupes = col_b.checkbox("Remove duplicates", value=True)
if st.button("Clean Data"):
clean_df = df.copy()
n_before = len(clean_df)
if drop_dupes: clean_df = clean_df.drop_duplicates()
if drop_nulls: clean_df = clean_df.dropna()
# Remove negative quantities
if "quantity" in clean_df.columns:
clean_df = clean_df[clean_df["quantity"] >= 0]
n_after = len(clean_df)
st.success(f"Cleaned: {n_before} -> {n_after} rows ({n_before-n_after} removed)")
st.dataframe(clean_df.head(10), use_container_width=True)
csv = clean_df.to_csv(index=False).encode()
st.download_button("Download Clean Data", csv, "clean_data.csv", "text/csv")
# upload_filter_download.py
import streamlit as st
import pandas as pd
import numpy as np
st.title("Upload, Filter & Download")
uploaded = st.file_uploader("Upload CSV", type=["csv"])
if uploaded:
df = pd.read_csv(uploaded)
# 1. Show df.shape and df.describe()
# 2. Select numeric column with st.selectbox
# 3. Add st.slider for min threshold
# 4. Filter df where col >= threshold
# 5. st.dataframe for filtered result
# 6. st.download_button for filtered CSV
Streamlit natively supports multi-page apps using the pages/ folder structure. st.navigation() (new API) or the legacy pages/ directory enables organized, scalable applications.
pages/ Directory Structure
# Multi-page app structure:
# my_app/
# app.py (main entry, shown as Home)
# pages/
# 1_Dashboard.py
# 2_Analytics.py
# 3_Settings.py
# app.py (main page / Home)
import streamlit as st
st.set_page_config(
page_title="My Data App",
page_icon="DATA",
layout="wide",
initial_sidebar_state="expanded"
)
st.title("Home - Data Analytics Platform")
st.write("Welcome to the multi-page data app!")
# Shared sidebar content (appears on all pages)
with st.sidebar:
st.image("https://streamlit.io/images/brand/streamlit-logo-primary-colormark-darktext.png",
width=150)
st.write("Navigation: Use the links above")
st.write("---")
col1, col2, col3 = st.columns(3)
col1.info("Dashboard\nView key metrics and charts")
col2.info("Analytics\nDeep-dive analysis tools")
col3.info("Settings\nConfigure your preferences")
# Shared state across pages
if "shared_data" not in st.session_state:
st.session_state.shared_data = {"user": "Guest", "theme": "light"}
st.write(f"Logged in as: **{st.session_state.shared_data['user']}**")
new_name = st.text_input("Change username:")
if new_name:
st.session_state.shared_data["user"] = new_name
st.success(f"Username updated to: {new_name}")
Programmatic Navigation with st.navigation()
# Modern multi-page app using st.navigation() (Streamlit >= 1.36)
# app_modern.py
import streamlit as st
import pandas as pd
import numpy as np
# Define pages programmatically
def home_page():
st.title("Home")
st.write("Welcome to the modern multi-page app!")
st.metric("Total Users", "1,234", "+15%")
st.metric("Daily Active", "456", "+8%")
def dashboard_page():
st.title("Dashboard")
np.random.seed(42)
data = pd.DataFrame(np.random.randn(50, 3), columns=["A", "B", "C"])
st.line_chart(data)
st.dataframe(data.describe())
def settings_page():
st.title("Settings")
if "settings" not in st.session_state:
st.session_state.settings = {"theme": "light", "language": "en"}
theme = st.selectbox("Theme", ["light", "dark"])
language = st.selectbox("Language", ["en", "fr", "de", "es"])
if st.button("Save Settings"):
st.session_state.settings = {"theme": theme, "language": language}
st.success("Settings saved!")
# Usage (Streamlit >= 1.36):
# pg = st.navigation({
# "App": [
# st.Page(home_page, title="Home", icon="HOME"),
# st.Page(dashboard_page, title="Dashboard", icon="CHART"),
# st.Page(settings_page, title="Settings", icon="GEAR"),
# ]
# })
# pg.run()
# For compatibility, show the structure:
page = st.selectbox("Simulate Navigation", ["Home", "Dashboard", "Settings"])
if page == "Home": home_page()
elif page == "Dashboard": dashboard_page()
else: settings_page()
Shared State & Authentication
# pages/shared_state_auth.py
import streamlit as st
import pandas as pd
import numpy as np
# Simulated authentication system
USERS = {
"admin": {"password": "admin123", "role": "admin"},
"user": {"password": "user123", "role": "viewer"},
}
def login_page():
st.title("Login")
with st.form("login_form"):
username = st.text_input("Username")
password = st.text_input("Password", type="password")
submitted = st.form_submit_button("Login")
if submitted:
if username in USERS and USERS[username]["password"] == password:
st.session_state.logged_in = True
st.session_state.username = username
st.session_state.role = USERS[username]["role"]
st.success(f"Welcome, {username}!")
st.rerun()
else:
st.error("Invalid credentials")
def protected_page():
st.title(f"Protected Dashboard")
st.write(f"Role: **{st.session_state.get('role', 'unknown')}**")
if st.session_state.get("role") == "admin":
st.success("Admin access: full data visible")
np.random.seed(42)
df = pd.DataFrame({
"user_id": range(10),
"revenue": np.random.exponential(1000, 10).round(2),
"secret_field": np.random.randint(0, 100, 10), # admin-only
})
st.dataframe(df)
else:
st.info("Viewer access: limited data")
df = pd.DataFrame({"product": ["A","B","C"], "revenue": [1000, 2000, 1500]})
st.dataframe(df)
if st.sidebar.button("Logout"):
for key in ["logged_in", "username", "role"]:
st.session_state.pop(key, None)
st.rerun()
# Main logic
if not st.session_state.get("logged_in", False):
login_page()
else:
protected_page()
# streamlit_multipage_rw.py
import streamlit as st
import pandas as pd
import plotly.express as px
import numpy as np
st.set_page_config(page_title="Analytics Platform", layout="wide")
# Initialize shared state
if "df" not in st.session_state:
np.random.seed(42)
n = 500
st.session_state.df = pd.DataFrame({
"date": pd.date_range("2024-01-01", periods=n, freq="D"),
"product": np.random.choice(["A","B","C","D"], n),
"region": np.random.choice(["North","South","East","West"], n),
"revenue": np.random.exponential(500, n).round(2),
"units": np.random.randint(1, 100, n),
})
if "filtered_df" not in st.session_state:
st.session_state.filtered_df = st.session_state.df.copy()
# Sidebar navigation
page = st.sidebar.selectbox("Page", ["Home", "Analysis", "Export"])
df = st.session_state.df
if page == "Home":
st.title("Overview Dashboard")
c1, c2, c3, c4 = st.columns(4)
c1.metric("Total Revenue", f"${df['revenue'].sum():,.0f}")
c2.metric("Total Units", f"{df['units'].sum():,}")
c3.metric("Products", df["product"].nunique())
c4.metric("Days", df["date"].nunique())
st.plotly_chart(px.line(df.groupby("date")["revenue"].sum().reset_index(),
x="date", y="revenue", title="Daily Revenue"), use_container_width=True)
elif page == "Analysis":
st.title("Interactive Analysis")
with st.sidebar:
products = st.multiselect("Products", df["product"].unique(), default=df["product"].unique())
regions = st.multiselect("Regions", df["region"].unique(), default=df["region"].unique())
filtered = df[df["product"].isin(products) & df["region"].isin(regions)]
st.session_state.filtered_df = filtered
st.write(f"Filtered: {len(filtered)} rows")
chart = st.selectbox("Chart", ["Revenue by Product", "Revenue by Region", "Scatter"])
if chart == "Revenue by Product":
st.plotly_chart(px.bar(filtered.groupby("product")["revenue"].sum().reset_index(), x="product", y="revenue"), use_container_width=True)
elif chart == "Revenue by Region":
st.plotly_chart(px.pie(filtered.groupby("region")["revenue"].sum().reset_index(), names="region", values="revenue"), use_container_width=True)
else:
st.plotly_chart(px.scatter(filtered, x="units", y="revenue", color="product"), use_container_width=True)
elif page == "Export":
st.title("Export Reports")
filtered = st.session_state.filtered_df
st.write(f"Exporting {len(filtered)} filtered rows")
st.download_button("Download CSV", filtered.to_csv(index=False).encode(), "report.csv", "text/csv")
# multipage_sim.py
import streamlit as st
import numpy as np
# Initialize shared state
if "shared_value" not in st.session_state:
st.session_state.shared_value = 0
page = st.sidebar.selectbox("Navigate to", ["Page 1", "Page 2", "Page 3"])
if page == "Page 1":
# 1. Show title "Page 1: Input"
# 2. st.number_input -> update st.session_state.shared_value
pass
elif page == "Page 2":
# 3. Show title "Page 2: View"
# 4. Show st.session_state.shared_value as st.metric
pass
else:
# 5. Show title "Page 3: Analysis"
# 6. Show chart using st.session_state.shared_value
pass
Streamlit supports custom CSS via st.markdown() with unsafe_allow_html=True, config.toml themes, and custom components for advanced UI needs.
Custom CSS with st.markdown()
# streamlit_custom_css.py
import streamlit as st
import pandas as pd
import numpy as np
st.title("Custom CSS & Styling")
# Inject custom CSS
st.markdown('''
<style>
/* Custom card styling */
.custom-card {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border: 1px solid #0f3460;
border-radius: 12px;
padding: 20px;
margin: 10px 0;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.2);
}
.card-title {
color: #00d4ff;
font-size: 1.3em;
font-weight: bold;
margin-bottom: 8px;
}
.card-value {
color: #ffffff;
font-size: 2em;
font-weight: bold;
}
.card-delta {
color: #4caf50;
font-size: 0.9em;
}
/* Custom button */
.stButton > button {
background: linear-gradient(90deg, #0077ff, #00d4ff);
color: white;
border: none;
border-radius: 25px;
padding: 8px 25px;
font-weight: bold;
transition: all 0.3s ease;
}
.stButton > button:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(0, 119, 255, 0.4);
}
/* Custom metric */
.highlight-box {
background: #0d1117;
border-left: 4px solid #00d4ff;
padding: 12px 16px;
margin: 8px 0;
border-radius: 0 8px 8px 0;
}
</style>
''', unsafe_allow_html=True)
# Custom cards
col1, col2, col3 = st.columns(3)
metrics = [
("Revenue", "$124,500", "+12.3%"),
("Users", "8,421", "+5.7%"),
("Conversion", "3.8%", "+0.4%"),
]
for col, (title, value, delta) in zip([col1, col2, col3], metrics):
col.markdown(f'''
<div class="custom-card">
<div class="card-title">{title}</div>
<div class="card-value">{value}</div>
<div class="card-delta">β {delta}</div>
</div>
''', unsafe_allow_html=True)
# Highlighted boxes
for item in ["Tip: Use st.markdown() with unsafe_allow_html=True for custom HTML",
"Tip: Define CSS in a single st.markdown() at the top of your app",
"Tip: Use class names to target specific Streamlit elements"]:
st.markdown(f'<div class="highlight-box">{item}</div>', unsafe_allow_html=True)
Theming via config.toml
# .streamlit/config.toml - controls app-wide theme
config_toml = '''
[theme]
primaryColor = "#00d4ff"
backgroundColor = "#0d1117"
secondaryBackgroundColor = "#161b22"
textColor = "#e6edf3"
font = "sans serif"
[server]
port = 8501
headless = true
maxUploadSize = 200
[browser]
gatherUsageStats = false
[runner]
fastReruns = true
'''
# streamlit_theming_demo.py
import streamlit as st
import plotly.express as px
import pandas as pd
import numpy as np
st.title("Theming Demo")
st.caption("Themes defined in .streamlit/config.toml")
# Display config structure
st.code(config_toml, language="toml")
# Programmatic theming tips
st.subheader("Dynamic Theming Tricks")
# Custom color palette for charts
COLORS = {
"dark": {"bg": "#0d1117", "text": "#e6edf3", "accent": "#00d4ff"},
"light": {"bg": "#ffffff", "text": "#0d1117", "accent": "#0077ff"},
}
theme = st.radio("Preview Theme", ["dark", "light"], horizontal=True)
colors = COLORS[theme]
# Apply to Plotly chart
np.random.seed(42)
df = pd.DataFrame({"x": range(20), "y": np.cumsum(np.random.randn(20))})
fig = px.line(df, x="x", y="y", title="Themed Chart")
fig.update_layout(
paper_bgcolor=colors["bg"],
plot_bgcolor=colors["bg"],
font_color=colors["text"],
)
fig.update_traces(line_color=colors["accent"])
st.plotly_chart(fig, use_container_width=True)
Styled Components & HTML Templates
# streamlit_styled_components.py
import streamlit as st
import pandas as pd
import numpy as np
st.title("Styled HTML Components")
# Progress bar with custom styling
def styled_progress(label, value, max_val=100, color="#00d4ff"):
pct = int(value / max_val * 100)
st.markdown(f'''
<div style="margin-bottom: 12px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span style="color: #e6edf3;">{label}</span>
<span style="color: {color}; font-weight: bold;">{value}/{max_val} ({pct}%)</span>
</div>
<div style="background: #161b22; border-radius: 8px; height: 12px; overflow: hidden;">
<div style="width: {pct}%; height: 100%; background: linear-gradient(90deg, {color}, {color}88);
border-radius: 8px; transition: width 0.5s ease;"></div>
</div>
</div>
''', unsafe_allow_html=True)
st.subheader("Custom Progress Bars")
styled_progress("Model Accuracy", 92, 100, "#4caf50")
styled_progress("Training Progress", 67, 100, "#00d4ff")
styled_progress("Data Quality", 78, 100, "#ff9800")
# Status badges
def badge(text, color="blue"):
colors = {"blue": "#0077ff", "green": "#4caf50", "red": "#f44336",
"yellow": "#ff9800", "purple": "#9c27b0"}
c = colors.get(color, colors["blue"])
return f'<span style="background:{c}22;color:{c};border:1px solid {c};border-radius:12px;padding:2px 10px;font-size:0.82em;font-weight:600;">{text}</span>'
st.subheader("Status Badges")
statuses = [("Production", "green"), ("Beta", "blue"), ("Deprecated", "red"),
("Warning", "yellow"), ("Experimental", "purple")]
badges_html = " ".join(badge(name, color) for name, color in statuses)
st.markdown(badges_html, unsafe_allow_html=True)
# Notification cards
def notify(message, type_="info"):
icons = {"info": "INFO", "success": "DONE", "warning": "WARN", "error": "ERR"}
bg_colors = {"info": "#0077ff", "success": "#4caf50", "warning": "#ff9800", "error": "#f44336"}
st.markdown(f'''
<div style="display:flex;align-items:center;padding:10px 15px;
border-radius:8px;background:{bg_colors[type_]}22;
border-left:4px solid {bg_colors[type_]};margin:6px 0;">
<span style="font-weight:bold;color:{bg_colors[type_]};margin-right:8px;">{icons[type_]}</span>
<span style="color:#e6edf3;">{message}</span>
</div>''', unsafe_allow_html=True)
st.subheader("Custom Notifications")
notify("Model training completed successfully!", "success")
notify("Dataset has 5% missing values. Consider imputation.", "warning")
notify("API rate limit approaching: 850/1000 requests used.", "info")
notify("Database connection failed. Using cached data.", "error")
# streamlit_styled_dashboard.py
import streamlit as st
import pandas as pd
import plotly.express as px
import numpy as np
st.set_page_config(layout="wide", page_title="ML Dashboard")
# Custom CSS
st.markdown('''
<style>
.metric-card {
background: linear-gradient(135deg, #1a1a2e, #16213e);
border: 1px solid #0f3460;
border-radius: 10px;
padding: 15px;
text-align: center;
}
.metric-label { color: #8b949e; font-size: 0.85em; }
.metric-value { color: #e6edf3; font-size: 1.8em; font-weight: bold; }
.metric-delta { color: #4caf50; font-size: 0.9em; }
.status-badge {
display: inline-block;
padding: 2px 12px;
border-radius: 12px;
font-size: 0.8em;
font-weight: 600;
}
</style>
''', unsafe_allow_html=True)
st.title("ML Model Health Dashboard")
# Model status
np.random.seed(42)
models = [
{"name": "Fraud Detector", "accuracy": 0.947, "status": "Production", "drift": 0.03},
{"name": "Churn Predictor", "accuracy": 0.823, "status": "Warning", "drift": 0.18},
{"name": "Recommender", "accuracy": 0.891, "status": "Production", "drift": 0.05},
{"name": "Price Model", "accuracy": 0.712, "status": "Degraded", "drift": 0.31},
]
st.subheader("Model Status")
cols = st.columns(len(models))
status_colors = {"Production": "#4caf50", "Warning": "#ff9800", "Degraded": "#f44336"}
for col, model in zip(cols, models):
color = status_colors.get(model["status"], "#0077ff")
col.markdown(f'''
<div class="metric-card">
<div class="metric-label">{model["name"]}</div>
<div class="metric-value">{model["accuracy"]:.1%}</div>
<div><span class="status-badge" style="background:{color}22;color:{color};border:1px solid {color};">
{model["status"]}</span></div>
<div class="metric-delta">Drift: {model["drift"]:.0%}</div>
</div>''', unsafe_allow_html=True)
# styled_component.py
import streamlit as st
st.title("Custom Styled Components")
# 1. Add CSS via st.markdown() with unsafe_allow_html=True
# 2. Create a styled_progress(label, value, color) function using HTML
# 3. Create a badge(text, color) function that returns an HTML span
# 4. Render 3 progress bars with different colors and 3 status badges
metrics = [("Accuracy", 92), ("F1 Score", 87), ("AUC", 95)]
statuses = [("Active", "green"), ("Inactive", "red"), ("Pending", "yellow")]
Deploy Streamlit apps to Streamlit Cloud (free), Docker containers, AWS/GCP, or Heroku. Production deployments need proper secret management, health checks, and logging.
Streamlit Cloud Deployment
# Complete deployment package for Streamlit Cloud
# File structure:
# my_app/
# app.py
# requirements.txt
# .streamlit/
# config.toml
# secrets.toml (git-ignored)
# requirements.txt
requirements = '''
streamlit>=1.32.0
pandas>=2.0.0
plotly>=5.18.0
scikit-learn>=1.4.0
numpy>=1.26.0
'''
# .streamlit/config.toml
config = '''
[server]
maxUploadSize = 200
headless = true
port = 8501
[theme]
primaryColor = "#0077ff"
backgroundColor = "#0d1117"
secondaryBackgroundColor = "#161b22"
textColor = "#e6edf3"
'''
# .streamlit/secrets.toml (local dev only, add to .gitignore)
secrets_example = '''
# .streamlit/secrets.toml (DO NOT COMMIT)
DATABASE_URL = "postgresql://user:pass@host:5432/db"
API_KEY = "your-api-key-here"
'''
# app.py - access secrets
import streamlit as st
st.title("Production-Ready Streamlit App")
# Access secrets (set in Streamlit Cloud dashboard or secrets.toml)
# api_key = st.secrets["API_KEY"]
# db_url = st.secrets["DATABASE_URL"]
# Show deployment info
st.info("Deployment: Streamlit Cloud")
st.code(requirements, language="text")
st.write("Deploy steps:")
steps = [
"1. Push code to GitHub",
"2. Go to share.streamlit.io",
"3. Connect GitHub repository",
"4. Set app.py as main file",
"5. Add secrets in the cloud dashboard",
"6. Click Deploy!",
]
for step in steps:
st.write(step)
Docker Deployment
# Docker deployment for Streamlit
# Dockerfile
dockerfile = '''
FROM python:3.10-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Copy and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Expose port
EXPOSE 8501
# Health check
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health || exit 1
# Run app
ENTRYPOINT ["streamlit", "run", "app.py", \
"--server.port=8501", \
"--server.address=0.0.0.0", \
"--server.headless=true"]
'''
# docker-compose.yml
docker_compose = '''
version: '3.8'
services:
streamlit:
build: .
ports:
- "8501:8501"
environment:
- API_KEY=${API_KEY}
volumes:
- ./data:/app/data
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8501/_stcore/health"]
interval: 30s
timeout: 10s
retries: 3
'''
import streamlit as st
st.title("Docker Deployment Guide")
st.subheader("Dockerfile")
st.code(dockerfile, language="docker")
st.subheader("docker-compose.yml")
st.code(docker_compose, language="yaml")
st.write("Build and run:")
st.code('''
# Build image
docker build -t my-streamlit-app .
# Run container
docker run -p 8501:8501 my-streamlit-app
# With docker-compose
docker-compose up -d
docker-compose logs -f streamlit
''', language="bash")
Production Best Practices
import streamlit as st
import pandas as pd
import numpy as np
import logging
import time
from functools import wraps
st.title("Production Best Practices")
# 1. Logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 2. Error handling
def safe_execute(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
st.error(f"Error: {e}")
logger.exception(f"Error in {func.__name__}")
return None
return wrapper
@safe_execute
def load_data_safely(source="demo"):
if source == "demo":
np.random.seed(42)
return pd.DataFrame({
"product": np.random.choice(["A","B","C"], 100),
"revenue": np.random.exponential(500, 100).round(2),
})
raise ValueError(f"Unknown source: {source}")
# 3. Performance monitoring
def timed(name):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
elapsed = (time.time() - t0) * 1000
logger.info(f"{name}: {elapsed:.0f}ms")
return result
return wrapper
return decorator
@timed("data_load")
@st.cache_data(ttl=300)
def get_dashboard_data():
return load_data_safely("demo")
df = get_dashboard_data()
if df is not None:
st.success(f"Data loaded: {len(df)} rows")
st.dataframe(df.head())
# 4. Health check endpoint pattern
st.subheader("Health Check Pattern")
st.code('''
# Add to app.py for health monitoring
@st.cache_data(ttl=60)
def health_status():
return {
"status": "healthy",
"model_loaded": model is not None,
"db_connected": check_db_connection(),
"cache_size": len(st.session_state),
}
# Show in sidebar
with st.sidebar:
if st.button("Health Check"):
st.json(health_status())
''')
# 5. Production checklist
st.subheader("Deployment Checklist")
checklist = [
("Add .streamlit/secrets.toml to .gitignore", True),
("Set all secrets via cloud dashboard or env vars", True),
("Pin dependency versions in requirements.txt", True),
("Add @st.cache_data to all data-loading functions", True),
("Test with st run app.py --server.headless=true", True),
("Set up health check endpoint", True),
("Configure error monitoring (Sentry, DataDog)", False),
("Set up CI/CD pipeline for auto-deployment", False),
]
for item, done in checklist:
icon = "CHECK" if done else "PENDING"
st.write(f" [{icon}] {item}")
# streamlit_production_app.py
import streamlit as st
import pandas as pd
import numpy as np
import logging
import time
import json
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s"
)
logger = logging.getLogger(__name__)
st.set_page_config(
page_title="Production ML Dashboard",
page_icon="CHART",
layout="wide",
)
# Global error handler
def handle_error(e, context=""):
logger.error(f"Error in {context}: {e}")
st.error(f"An error occurred: {e}")
if st.button("Show Error Details"):
st.exception(e)
# Health check
@st.cache_data(ttl=30)
def get_health():
return {
"status": "healthy",
"timestamp": time.time(),
"session_vars": len(st.session_state),
}
# Model loading
@st.cache_resource
def load_model():
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
logger.info("Loading ML model")
X, y = load_iris(return_X_y=True)
return RandomForestClassifier(n_estimators=50, random_state=42).fit(X, y)
# Data loading with caching
@st.cache_data(ttl=300)
def load_dashboard_data():
logger.info("Loading dashboard data")
np.random.seed(42)
n = 1000
return pd.DataFrame({
"date": pd.date_range("2024-01-01", periods=n, freq="D")[:n],
"product": np.random.choice(["A","B","C"], n),
"revenue": np.random.exponential(500, n).round(2),
"units": np.random.randint(1, 100, n),
})
# Main app
try:
model = load_model()
df = load_dashboard_data()
st.title("Production ML Dashboard")
# KPIs
c1, c2, c3 = st.columns(3)
c1.metric("Total Revenue", f"${df['revenue'].sum():,.0f}")
c2.metric("Total Units", f"{df['units'].sum():,}")
c3.metric("Model Status", "Healthy")
# Chart
monthly = df.groupby(df["date"].dt.month)["revenue"].sum()
st.bar_chart(monthly)
# Health sidebar
with st.sidebar:
st.title("System Health")
health = get_health()
st.json(health)
logger.info("Dashboard rendered successfully")
except Exception as e:
handle_error(e, "main app")
# production_app.py
import streamlit as st
import pandas as pd
import numpy as np
import time
st.title("Production App")
# 1. @st.cache_data(ttl=300) - load_data() returns a DataFrame
# 2. @st.cache_resource - load_model() returns a trained sklearn model
# 3. try/except wrapper around main app logic
# 4. Sidebar with "Health Check" button that shows:
# {"status": "healthy", "data_rows": len(df), "model": type(model).__name__}