Post

🌊 Streamlit: Deep Dive & Best Practices

Concise, clear, and validated revision notes on Pandas β€” structured for beginners and practitioners.

🌊 Streamlit: Deep Dive & Best Practices

Streamlit: Deep Dive & Best Practices

Introduction

Streamlit is an open-source Python framework that revolutionizes how data scientists and machine learning engineers create and share interactive web applications. Unlike traditional web frameworks that require expertise in HTML, CSS, JavaScript, and backend development, Streamlit allows you to build beautiful, production-ready data apps using pure Pythonβ€”often in just a few lines of code. Created to bridge the gap between data analysis and web deployment, Streamlit automatically handles the entire web stack, from UI rendering to state management, enabling data professionals to focus on what matters most: their data and insights. This comprehensive guide explores Streamlit from fundamental concepts to advanced deployment strategies, empowering you to rapidly prototype, iterate, and deploy data-driven applications.


Table 1: Streamlit Ecosystem Terminology Mapping

Different contexts and documentation use varying terminology for similar Streamlit concepts:

Standard TermAlternative NamesContext/Usage
WidgetInput Element, Control, Interactive ComponentUser input elements
RerunScript Re-execution, Refresh, Update CycleScript execution model
Session StateApp State, Persistent State, State ManagementData persistence across reruns
Magic CommandsAutomatic Display, Implicit RenderingVariables displayed without st.write()
st.write()Swiss Army Knife, Universal DisplayVersatile display function
CachingMemoization, Cached Computation, Performance OptimizationAvoiding redundant computations
@st.cache_dataData Cache, DataFrame CacheCaching data transformations
@st.cache_resourceResource Cache, Connection CacheCaching database/ML model connections
LayoutPage Structure, Container OrganizationOrganizing app components
SidebarSide Panel, Navigation PanelSecondary content area
ColumnGrid Column, Layout ColumnHorizontal layout division
ContainerLayout Container, Group ContainerLogical grouping of elements
ExpanderCollapsible Section, AccordionExpandable content section
TabTab Panel, Tabbed InterfaceTabbed content organization
FormBatch Input, Input GroupGrouped input submission
Progress BarProgress Indicator, Loading BarVisual progress feedback
SpinnerLoading Indicator, Wait SpinnerLoading state indicator
ComponentCustom Component, Streamlit ComponentJavaScript-Python bridge
Community CloudStreamlit Cloud, Streamlit SharingFree hosting platform

Table 2: Hierarchical Streamlit Concept Structure

This table organizes Streamlit concepts from high-level abstractions to specific implementations:

LevelCategoryTermParent ConceptDescription
L1FrameworkStreamlit-Python web framework for data apps
L2Execution ModelScript-based ExecutionStreamlitTop-to-bottom script execution
L2Execution ModelReactive UpdatesStreamlitAutomatic rerun on interaction
L2Execution ModelSession StateStreamlitState persistence across reruns
L3Display FunctionsText ElementsDisplayTitle, header, markdown, text
L3Display FunctionsData DisplayDisplayDataFrame, table, JSON, metric
L3Display FunctionsChart ElementsDisplayLine chart, bar chart, scatter plot
L3Display FunctionsMedia ElementsDisplayImage, audio, video
L4Text Elementsst.title()Text DisplayLarge title text
L4Text Elementsst.header()Text DisplaySection header
L4Text Elementsst.subheader()Text DisplaySubsection header
L4Text Elementsst.markdown()Text DisplayMarkdown text
L4Text Elementsst.write()Text DisplayUniversal display function
L4Text Elementsst.code()Text DisplayCode block with syntax highlighting
L4Text Elementsst.latex()Text DisplayLaTeX mathematical notation
L5Data Displayst.dataframe()Data ElementsInteractive DataFrame
L5Data Displayst.table()Data ElementsStatic table
L5Data Displayst.json()Data ElementsJSON display
L5Data Displayst.metric()Data ElementsMetric card with delta
L6Chart Elementsst.line_chart()VisualizationSimple line chart
L6Chart Elementsst.bar_chart()VisualizationSimple bar chart
L6Chart Elementsst.area_chart()VisualizationSimple area chart
L6Chart Elementsst.pyplot()VisualizationMatplotlib figures
L6Chart Elementsst.plotly_chart()VisualizationPlotly interactive charts
L6Chart Elementsst.altair_chart()VisualizationAltair declarative charts
L6Chart Elementsst.map()VisualizationGeographic data visualization
L7Input Widgetsst.button()User InputClickable button
L7Input Widgetsst.checkbox()User InputBoolean checkbox
L7Input Widgetsst.radio()User InputSingle selection radio
L7Input Widgetsst.selectbox()User InputDropdown selection
L7Input Widgetsst.multiselect()User InputMultiple selection
L7Input Widgetsst.slider()User InputRange slider
L7Input Widgetsst.text_input()User InputSingle-line text
L7Input Widgetsst.text_area()User InputMulti-line text
L7Input Widgetsst.number_input()User InputNumeric input
L7Input Widgetsst.date_input()User InputDate picker
L7Input Widgetsst.time_input()User InputTime picker
L7Input Widgetsst.file_uploader()User InputFile upload
L7Input Widgetsst.camera_input()User InputCamera capture
L7Input Widgetsst.color_picker()User InputColor selection
L8Layout Elementsst.sidebarLayoutSidebar container
L8Layout Elementsst.columns()LayoutHorizontal columns
L8Layout Elementsst.container()LayoutLogical container
L8Layout Elementsst.expander()LayoutCollapsible section
L8Layout Elementsst.tabs()LayoutTabbed interface
L8Layout Elementsst.form()LayoutBatch input submission
L9Status Elementsst.success()FeedbackSuccess message
L9Status Elementsst.info()FeedbackInformation message
L9Status Elementsst.warning()FeedbackWarning message
L9Status Elementsst.error()FeedbackError message
L9Status Elementsst.exception()FeedbackException display
L9Status Elementsst.progress()FeedbackProgress bar
L9Status Elementsst.spinner()FeedbackLoading spinner
L9Status Elementsst.balloons()FeedbackCelebratory animation
L9Status Elementsst.snow()FeedbackSnow animation
L10Caching@st.cache_dataPerformanceCache data transformations
L10Caching@st.cache_resourcePerformanceCache connections/models
L10Statest.session_stateState ManagementPersistent variables
L11AdvancedCustom ComponentsExtensibilityJavaScript-Python integration
L11AdvancedMulti-page AppsOrganizationMultiple app pages
L11AdvancedThemingCustomizationCustom color schemes

Streamlit App Lifecycle

Understanding how Streamlit executes your code is crucial for building efficient applications.

Complete Execution Flow

graph TD
    A[User Opens App] --> B[Streamlit Server Starts]
    B --> C[Execute Script Top-to-Bottom]
    C --> D[Render Elements in Order]
    D --> E[Display App in Browser]
    
    E --> F{User Interaction?}
    F -->|No| E
    F -->|Yes - Widget Change| G[Trigger Rerun]
    F -->|Yes - Button Click| G
    
    G --> H[Clear Current Display]
    H --> I[Re-execute Entire Script]
    I --> J[Read Session State]
    J --> K[Execute Cached Functions]
    K --> L[Execute Non-cached Code]
    L --> M[Update Session State]
    M --> D
    
    style C fill:#e1ffe1
    style G fill:#ffe1e1
    style I fill:#fff4e1
    style K fill:#e1f5ff

Key Characteristics:

  1. Top-to-Bottom Execution: Script runs from first line to last
  2. Automatic Reruns: Any widget interaction triggers complete rerun
  3. Stateless by Default: Variables reset on each rerun (unless using session_state)
  4. Caching: Expensive computations cached to avoid redundant work
  5. Sequential Rendering: Elements displayed in code order

Phase 1: Installation and Setup

1.1 Installation

1
2
3
4
5
6
7
8
9
10
11
# Install Streamlit
pip install streamlit

# Install with additional dependencies
pip install streamlit pandas numpy matplotlib plotly

# Verify installation
streamlit --version

# Run hello world demo
streamlit hello

1.2 First Streamlit App

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# app.py
import streamlit as st

# Title
st.title("My First Streamlit App 🎈")

# Simple text
st.write("Hello, World!")

# Interactive widget
name = st.text_input("What's your name?")

if name:
    st.write(f"Hello, {name}!")

Running the App:

1
2
3
4
5
6
7
8
9
10
11
# Run from command line
streamlit run app.py

# Run with custom port
streamlit run app.py --server.port 8080

# Run with custom host
streamlit run app.py --server.address 0.0.0.0

# Run without browser auto-open
streamlit run app.py --server.headless true

Access Points:

  • Local URL: http://localhost:8501
  • Network URL: Displays in terminal for sharing
  • Can manually refresh or enable auto-refresh

Phase 2: Display Elements

2.1 Text Elements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import streamlit as st

# Title (largest)
st.title("This is a Title")

# Header
st.header("This is a Header")

# Subheader
st.subheader("This is a Subheader")

# Plain text
st.text("This is plain text")

# Markdown (supports full markdown syntax)
st.markdown("""
# H1 Heading
## H2 Heading

**Bold text** and *italic text*

- Bullet point 1
- Bullet point 2

[Link to Streamlit](https://streamlit.io)
""")

# Code block with syntax highlighting
st.code("""
def hello_world():
    print("Hello, World!")
""", language="python")

# LaTeX mathematical notation
st.latex(r'''
a^2 + b^2 = c^2
''')

# Caption (small text)
st.caption("This is a caption")

# Magic commands (automatic display)
"This text is automatically displayed!"
x = 42  # This won't display
x  # This WILL display (magic!)

2.2 Data Display

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import streamlit as st
import pandas as pd
import numpy as np

# Create sample data
df = pd.DataFrame({
    'first column': [1, 2, 3, 4],
    'second column': [10, 20, 30, 40]
})

# Interactive DataFrame
st.dataframe(df)

# Styled DataFrame with highlighting
st.dataframe(df.style.highlight_max(axis=0))

# Static table (not interactive)
st.table(df)

# JSON display
json_data = {
    "name": "John",
    "age": 30,
    "city": "New York"
}
st.json(json_data)

# Metrics (KPI cards)
st.metric(
    label="Temperature",
    value="70 Β°F",
    delta="1.2 Β°F"
)

# Multiple metrics in columns
col1, col2, col3 = st.columns(3)
col1.metric("Revenue", "$45,231", "+12%")
col2.metric("Users", "1,234", "-8%")
col3.metric("Conversion", "3.2%", "+0.5%")

2.3 Chart and Visualization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px

# Sample data
chart_data = pd.DataFrame(
    np.random.randn(20, 3),
    columns=['a', 'b', 'c']
)

# Simple line chart
st.line_chart(chart_data)

# Simple bar chart
st.bar_chart(chart_data)

# Simple area chart
st.area_chart(chart_data)

# Matplotlib figure
fig, ax = plt.subplots()
ax.hist(np.random.randn(100), bins=20)
st.pyplot(fig)

# Plotly interactive chart
df = px.data.iris()
fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species")
st.plotly_chart(fig)

# Map visualization (requires lat/lon columns)
map_data = pd.DataFrame(
    np.random.randn(1000, 2) / [50, 50] + [37.76, -122.4],
    columns=['lat', 'lon']
)
st.map(map_data)

2.4 Media Elements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import streamlit as st
from PIL import Image

# Display image
image = Image.open('path/to/image.jpg')
st.image(image, caption='Sample Image', use_column_width=True)

# Display from URL
st.image('https://example.com/image.jpg')

# Display audio
audio_file = open('path/to/audio.mp3', 'rb')
st.audio(audio_file.read(), format='audio/mp3')

# Display video
video_file = open('path/to/video.mp4', 'rb')
st.video(video_file.read())

# Display from YouTube
st.video('https://www.youtube.com/watch?v=dQw4w9WgXcQ')

Phase 3: Input Widgets

3.1 Basic Widgets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import streamlit as st

# Button
if st.button('Click me'):
    st.write('Button clicked!')

# Checkbox
agree = st.checkbox('I agree to terms and conditions')
if agree:
    st.write('Great!')

# Radio buttons (single selection)
genre = st.radio(
    "What's your favorite movie genre?",
    ('Comedy', 'Drama', 'Documentary')
)
st.write(f'You selected: {genre}')

# Selectbox (dropdown)
option = st.selectbox(
    'How would you like to be contacted?',
    ('Email', 'Phone', 'Mobile')
)
st.write(f'You selected: {option}')

# Multiselect (multiple selections)
options = st.multiselect(
    'What are your favorite colors?',
    ['Green', 'Yellow', 'Red', 'Blue'],
    default=['Yellow', 'Red']
)
st.write(f'You selected: {options}')

# Slider
age = st.slider('How old are you?', 0, 100, 25)
st.write(f"I'm {age} years old")

# Range slider
values = st.slider(
    'Select a range of values',
    0.0, 100.0, (25.0, 75.0)
)
st.write('Range:', values)

3.2 Text and Numeric Input

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import streamlit as st

# Text input
name = st.text_input('Enter your name', 'John Doe')
st.write(f'Hello, {name}!')

# Text area (multiline)
message = st.text_area('Enter your message', 'Type here...')
st.write(f'Message length: {len(message)} characters')

# Number input
number = st.number_input('Enter a number', min_value=0, max_value=100, value=50, step=1)
st.write(f'You entered: {number}')

# Password input
password = st.text_input('Enter password', type='password')

3.3 Date and Time Input

1
2
3
4
5
6
7
8
9
10
import streamlit as st
from datetime import date, time

# Date input
d = st.date_input("When's your birthday", date(2000, 1, 1))
st.write('Your birthday is:', d)

# Time input
t = st.time_input('Set an alarm for', time(8, 45))
st.write('Alarm is set for:', t)

3.4 File Upload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import streamlit as st
import pandas as pd

# File uploader
uploaded_file = st.file_uploader("Choose a CSV file", type=['csv'])

if uploaded_file is not None:
    # Read file
    df = pd.read_csv(uploaded_file)
    st.write("File uploaded successfully!")
    st.dataframe(df)
    
    # File details
    st.write(f"Filename: {uploaded_file.name}")
    st.write(f"File size: {uploaded_file.size} bytes")

# Multiple file upload
uploaded_files = st.file_uploader(
    "Choose multiple files",
    accept_multiple_files=True
)

for uploaded_file in uploaded_files:
    st.write(f"Filename: {uploaded_file.name}")

# Camera input
camera_photo = st.camera_input("Take a picture")
if camera_photo:
    st.image(camera_photo)

3.5 Advanced Widgets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import streamlit as st

# Color picker
color = st.color_picker('Pick a color', '#00f900')
st.write(f'The current color is {color}')

# Download button
csv_data = df.to_csv(index=False)
st.download_button(
    label="Download data as CSV",
    data=csv_data,
    file_name='data.csv',
    mime='text/csv'
)

# Link button
st.link_button("Go to Streamlit", "https://streamlit.io")

Phase 4: Layout and Containers

4.1 Sidebar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import streamlit as st

# Add to sidebar
st.sidebar.title("Sidebar Title")
st.sidebar.write("This is in the sidebar")

# Sidebar widgets
option = st.sidebar.selectbox(
    'Select an option',
    ['Option 1', 'Option 2', 'Option 3']
)

# Sidebar slider
slider_value = st.sidebar.slider('Select a value', 0, 100, 50)

# Main content
st.title("Main Content")
st.write(f"Selected option: {option}")
st.write(f"Slider value: {slider_value}")

4.2 Columns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import streamlit as st

# Create two equal columns
col1, col2 = st.columns(2)

with col1:
    st.header("Column 1")
    st.write("This is column 1")
    st.button("Button 1")

with col2:
    st.header("Column 2")
    st.write("This is column 2")
    st.button("Button 2")

# Columns with custom widths
col1, col2, col3 = st.columns([3, 1, 1])
col1.write("This column is 3x wider")
col2.write("Column 2")
col3.write("Column 3")

4.3 Containers and Expanders

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import streamlit as st

# Container (logical grouping)
with st.container():
    st.write("This is inside a container")
    st.button("Container button")

# Expander (collapsible section)
with st.expander("Click to expand"):
    st.write("Hidden content that can be expanded")
    st.image("image.jpg")

# Multiple expanders
with st.expander("Section 1"):
    st.write("Content of section 1")

with st.expander("Section 2"):
    st.write("Content of section 2")

4.4 Tabs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import streamlit as st

# Create tabs
tab1, tab2, tab3 = st.tabs(["πŸ“ˆ Chart", "πŸ—ƒ Data", "πŸ“ Notes"])

with tab1:
    st.header("Chart Tab")
    st.line_chart([1, 2, 3, 4])

with tab2:
    st.header("Data Tab")
    st.write("Data goes here")

with tab3:
    st.header("Notes Tab")
    st.text_area("Enter your notes")

4.5 Forms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import streamlit as st

# Form (batch input submission)
with st.form("my_form"):
    st.write("Inside the form")
    
    name = st.text_input("Name")
    age = st.slider("Age", 0, 100, 25)
    submit = st.form_submit_button("Submit")
    
    if submit:
        st.write(f"Name: {name}, Age: {age}")

# Form outside with statement
form = st.form("another_form")
form.text_input("Email")
form.slider("Rating", 1, 5)
submitted = form.form_submit_button("Submit")

Phase 5: Session State

5.1 Understanding Session State

Session State allows you to persist variables across reruns.

1
2
3
4
5
6
7
8
9
10
11
12
13
import streamlit as st

# Initialize session state
if 'counter' not in st.session_state:
    st.session_state.counter = 0

# Button that increments counter
if st.button('Increment'):
    st.session_state.counter += 1

st.write(f"Counter: {st.session_state.counter}")

# Without session state, counter would reset to 0 on every click!

5.2 Session State Patterns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import streamlit as st

# Pattern 1: Simple counter
if 'count' not in st.session_state:
    st.session_state.count = 0

increment = st.button('Increment')
if increment:
    st.session_state.count += 1

decrement = st.button('Decrement')
if decrement:
    st.session_state.count -= 1

st.write('Count:', st.session_state.count)

# Pattern 2: Form with persistent data
if 'user_data' not in st.session_state:
    st.session_state.user_data = {}

name = st.text_input('Name', value=st.session_state.user_data.get('name', ''))
email = st.text_input('Email', value=st.session_state.user_data.get('email', ''))

if st.button('Save'):
    st.session_state.user_data = {'name': name, 'email': email}
    st.success('Data saved!')

# Pattern 3: Page navigation
if 'page' not in st.session_state:
    st.session_state.page = 'home'

if st.session_state.page == 'home':
    st.title("Home Page")
    if st.button('Go to Settings'):
        st.session_state.page = 'settings'
elif st.session_state.page == 'settings':
    st.title("Settings Page")
    if st.button('Go to Home'):
        st.session_state.page = 'home'

5.3 Callbacks with Session State

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import streamlit as st

# Callback function
def increment_counter():
    st.session_state.counter += 1

def decrement_counter():
    st.session_state.counter -= 1

# Initialize
if 'counter' not in st.session_state:
    st.session_state.counter = 0

# Buttons with callbacks
st.button('Increment', on_click=increment_counter)
st.button('Decrement', on_click=decrement_counter)

st.write(f"Counter: {st.session_state.counter}")

# Widget with callback
def on_change():
    st.session_state.last_selection = st.session_state.selectbox

option = st.selectbox(
    'Choose option',
    ['A', 'B', 'C'],
    key='selectbox',
    on_change=on_change
)

if 'last_selection' in st.session_state:
    st.write(f"Last selection: {st.session_state.last_selection}")

Phase 6: Caching and Performance

6.1 Understanding Caching

Streamlit caches function results to avoid redundant computations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import streamlit as st
import pandas as pd
import time

# Without caching (SLOW - runs every rerun)
def load_data_slow():
    time.sleep(5)  # Simulate expensive operation
    return pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})

# With caching (FAST - runs once, cached thereafter)
@st.cache_data
def load_data_fast():
    time.sleep(5)  # Only slow first time
    return pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})

# Use cached function
df = load_data_fast()
st.dataframe(df)

6.2 Cache Data vs Cache Resource

@st.cache_data - For data transformations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import streamlit as st
import pandas as pd

@st.cache_data
def load_csv(file_path):
    """Cache data loading"""
    return pd.read_csv(file_path)

@st.cache_data
def process_data(df):
    """Cache data processing"""
    # Expensive data transformation
    return df.groupby('category').sum()

# Use cached functions
df = load_csv('data.csv')
processed = process_data(df)
st.dataframe(processed)

@st.cache_resource - For connections and models:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import streamlit as st
import joblib
from sqlalchemy import create_engine

@st.cache_resource
def init_database():
    """Cache database connection"""
    return create_engine('postgresql://user:pass@localhost/db')

@st.cache_resource
def load_model():
    """Cache ML model"""
    return joblib.load('model.pkl')

# Use cached resources
db = init_database()
model = load_model()

# Make prediction
input_data = st.number_input('Input value')
prediction = model.predict([[input_data]])
st.write(f"Prediction: {prediction[0]}")

6.3 Cache Control

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import streamlit as st

# Cache with TTL (time to live)
@st.cache_data(ttl=3600)  # Cache for 1 hour
def get_api_data():
    # Fetch from API
    return data

# Cache with custom key
@st.cache_data
def load_data(file_path, columns):
    return pd.read_csv(file_path, usecols=columns)

# Clear specific cache
st.cache_data.clear()

# Clear all caches
st.cache_resource.clear()

# Manual cache control
if st.button("Clear Cache"):
    st.cache_data.clear()
    st.success("Cache cleared!")

Phase 7: Status and Progress

7.1 Status Messages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import streamlit as st

# Success message
st.success("Operation completed successfully!")

# Information message
st.info("This is an information message")

# Warning message
st.warning("This is a warning")

# Error message
st.error("An error occurred!")

# Exception display
try:
    result = 1 / 0
except Exception as e:
    st.exception(e)

7.2 Progress Indicators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import streamlit as st
import time

# Progress bar
progress_bar = st.progress(0)
for i in range(100):
    time.sleep(0.01)
    progress_bar.progress(i + 1)

st.success("Complete!")

# Spinner (loading indicator)
with st.spinner('Loading...'):
    time.sleep(3)
st.success('Done!')

# Status container
with st.status("Downloading data...") as status:
    st.write("Searching for data...")
    time.sleep(2)
    st.write("Found URL.")
    time.sleep(1)
    st.write("Downloading data...")
    time.sleep(1)
    status.update(label="Download complete!", state="complete", expanded=False)

7.3 Celebrations

1
2
3
4
5
6
7
8
9
import streamlit as st

# Balloons
if st.button("Celebrate!"):
    st.balloons()

# Snow
if st.button("Let it snow!"):
    st.snow()

Phase 8: Advanced Features

8.1 Multi-page Apps

Project Structure:

1
2
3
4
5
6
myapp/
β”œβ”€β”€ Home.py  (or main.py)
└── pages/
    β”œβ”€β”€ 1_πŸ“Š_Dashboard.py
    β”œβ”€β”€ 2_πŸ“ˆ_Analytics.py
    └── 3_βš™οΈ_Settings.py

Home.py:

1
2
3
4
5
6
7
8
9
10
11
import streamlit as st

st.set_page_config(
    page_title="My App",
    page_icon="🏠",
    layout="wide"
)

st.title("Home Page")
st.write("Welcome to my multi-page app!")
st.write("Use the sidebar to navigate")

pages/1_πŸ“Š_Dashboard.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
import streamlit as st
import pandas as pd
import numpy as np

st.title("Dashboard")

# Generate sample data
chart_data = pd.DataFrame(
    np.random.randn(20, 3),
    columns=['a', 'b', 'c']
)

st.line_chart(chart_data)

Run with: streamlit run Home.py

8.2 Configuration and Theming

.streamlit/config.toml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[theme]
primaryColor = "#F63366"
backgroundColor = "#FFFFFF"
secondaryBackgroundColor = "#F0F2F6"
textColor = "#262730"
font = "sans serif"

[server]
port = 8501
headless = false
enableCORS = false

[browser]
gatherUsageStats = false
serverAddress = "localhost"

Programmatic Configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import streamlit as st

# Page configuration
st.set_page_config(
    page_title="My App",
    page_icon="🎈",
    layout="wide",  # or "centered"
    initial_sidebar_state="expanded",  # or "collapsed"
    menu_items={
        'Get Help': 'https://www.example.com/help',
        'Report a bug': "https://www.example.com/bug",
        'About': "# This is a header. This is an *extremely* cool app!"
    }
)

8.3 Custom Components

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import streamlit as st
import streamlit.components.v1 as components

# Embed HTML
html_code = """
<div style="background-color: lightblue; padding: 10px;">
    <h2>Custom HTML Component</h2>
    <p>This is rendered from HTML</p>
</div>
"""
components.html(html_code, height=200)

# Embed iframe
components.iframe("https://www.example.com", height=600)

# JavaScript communication
js_code = """
<script>
    const value = Math.random();
    window.parent.postMessage({type: 'streamlit:setComponentValue', value: value}, '*');
</script>
"""
result = components.html(js_code, height=0)
if result:
    st.write(f"Random value from JS: {result}")

8.4 Query Parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import streamlit as st

# Get query parameters
query_params = st.query_params

# Read parameter
page = query_params.get("page", "home")
user_id = query_params.get("user_id", None)

st.write(f"Current page: {page}")
if user_id:
    st.write(f"User ID: {user_id}")

# Set query parameters
if st.button("Go to Dashboard"):
    st.query_params.update({"page": "dashboard"})
    st.rerun()

# URL: http://localhost:8501/?page=dashboard&user_id=123

Phase 9: Working with Data

9.1 Data Loading and Display

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import streamlit as st
import pandas as pd
import numpy as np

@st.cache_data
def load_data():
    # Load from CSV
    df = pd.read_csv('data.csv')
    return df

# Load and display
df = load_data()

# Interactive dataframe
st.dataframe(
    df,
    use_container_width=True,
    height=400
)

# Column configuration
st.dataframe(
    df,
    column_config={
        "price": st.column_config.NumberColumn(
            "Price (USD)",
            help="Product price in USD",
            format="$%.2f"
        ),
        "rating": st.column_config.ProgressColumn(
            "Rating",
            help="Customer rating",
            min_value=0,
            max_value=5
        ),
        "url": st.column_config.LinkColumn(
            "Website",
            display_text="Visit"
        )
    }
)

9.2 Data Editing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import streamlit as st
import pandas as pd

# Create sample dataframe
df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie'],
    'age': [25, 30, 35],
    'city': ['NYC', 'LA', 'Chicago']
})

# Editable dataframe
edited_df = st.data_editor(
    df,
    num_rows="dynamic",  # Allow adding/removing rows
    use_container_width=True
)

if st.button("Save Changes"):
    st.session_state.saved_data = edited_df
    st.success("Data saved!")

# Display saved data
if 'saved_data' in st.session_state:
    st.write("Saved data:")
    st.dataframe(st.session_state.saved_data)

9.3 Data Filtering

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import streamlit as st
import pandas as pd

@st.cache_data
def load_data():
    return pd.read_csv('data.csv')

df = load_data()

# Sidebar filters
st.sidebar.header("Filters")

# Numeric filter
min_age, max_age = st.sidebar.slider(
    'Select age range',
    int(df['age'].min()),
    int(df['age'].max()),
    (20, 50)
)

# Category filter
categories = st.sidebar.multiselect(
    'Select categories',
    options=df['category'].unique(),
    default=df['category'].unique()
)

# Apply filters
filtered_df = df[
    (df['age'] >= min_age) &
    (df['age'] <= max_age) &
    (df['category'].isin(categories))
]

# Display results
st.write(f"Showing {len(filtered_df)} of {len(df)} records")
st.dataframe(filtered_df)

9.4 Data Export

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import streamlit as st
import pandas as pd

df = pd.read_csv('data.csv')

# Export to CSV
csv = df.to_csv(index=False).encode('utf-8')
st.download_button(
    label="Download CSV",
    data=csv,
    file_name='data.csv',
    mime='text/csv'
)

# Export to Excel
from io import BytesIO

buffer = BytesIO()
with pd.ExcelWriter(buffer, engine='xlsxwriter') as writer:
    df.to_excel(writer, sheet_name='Sheet1', index=False)
    writer.close()

st.download_button(
    label="Download Excel",
    data=buffer,
    file_name='data.xlsx',
    mime='application/vnd.ms-excel'
)

# Export to JSON
json_data = df.to_json(orient='records')
st.download_button(
    label="Download JSON",
    data=json_data,
    file_name='data.json',
    mime='application/json'
)

Phase 10: Machine Learning Integration

10.1 Loading and Using ML Models

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import streamlit as st
import joblib
import pandas as pd

@st.cache_resource
def load_model():
    """Load trained model"""
    return joblib.load('model.pkl')

@st.cache_resource
def load_scaler():
    """Load feature scaler"""
    return joblib.load('scaler.pkl')

# Load resources
model = load_model()
scaler = load_scaler()

# UI for input
st.title("ML Model Prediction")

col1, col2 = st.columns(2)
with col1:
    feature1 = st.number_input("Feature 1", value=0.0)
    feature2 = st.number_input("Feature 2", value=0.0)

with col2:
    feature3 = st.number_input("Feature 3", value=0.0)
    feature4 = st.number_input("Feature 4", value=0.0)

if st.button("Predict"):
    # Prepare input
    input_data = pd.DataFrame({
        'feature1': [feature1],
        'feature2': [feature2],
        'feature3': [feature3],
        'feature4': [feature4]
    })
    
    # Scale features
    scaled_data = scaler.transform(input_data)
    
    # Make prediction
    prediction = model.predict(scaled_data)
    probability = model.predict_proba(scaled_data)
    
    # Display results
    st.success(f"Prediction: {prediction[0]}")
    st.write(f"Probability: {probability[0][1]:.2%}")

10.2 Real-time Predictions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import streamlit as st
import numpy as np
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier

@st.cache_resource
def train_model():
    """Train and cache model"""
    iris = load_iris()
    model = RandomForestClassifier()
    model.fit(iris.data, iris.target)
    return model, iris

model, iris = train_model()

st.title("Iris Flower Classifier")

# Sliders for input
sepal_length = st.slider('Sepal Length', 4.0, 8.0, 5.0)
sepal_width = st.slider('Sepal Width', 2.0, 4.5, 3.0)
petal_length = st.slider('Petal Length', 1.0, 7.0, 4.0)
petal_width = st.slider('Petal Width', 0.1, 2.5, 1.0)

# Real-time prediction (no button needed!)
input_data = np.array([[sepal_length, sepal_width, petal_length, petal_width]])
prediction = model.predict(input_data)[0]
probability = model.predict_proba(input_data)[0]

# Display prediction
st.subheader("Prediction")
st.write(f"Class: {iris.target_names[prediction]}")

# Display probabilities
st.subheader("Confidence")
for i, class_name in enumerate(iris.target_names):
    st.write(f"{class_name}: {probability[i]:.2%}")
    st.progress(float(probability[i]))

10.3 Model Comparison

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import streamlit as st
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report

st.title("Model Comparison")

# Load data
@st.cache_data
def load_data():
    # Your data loading logic
    return X, y

X, y = load_data()

# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# Model selection
models = {
    'Random Forest': RandomForestClassifier(),
    'Logistic Regression': LogisticRegression(),
    'SVM': SVC()
}

selected_model = st.selectbox("Select Model", list(models.keys()))

if st.button("Train and Evaluate"):
    with st.spinner(f"Training {selected_model}..."):
        model = models[selected_model]
        model.fit(X_train, y_train)
        
        # Predictions
        y_pred = model.predict(X_test)
        
        # Metrics
        accuracy = accuracy_score(y_test, y_pred)
        
        st.success(f"Training Complete!")
        st.metric("Accuracy", f"{accuracy:.2%}")
        
        # Classification report
        report = classification_report(y_test, y_pred, output_dict=True)
        st.dataframe(pd.DataFrame(report).transpose())

Phase 11: Best Practices

11.1 Code Organization

Recommended Structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# app.py
import streamlit as st
from utils import load_data, process_data
from models import predict

# Page config at the top
st.set_page_config(
    page_title="My App",
    layout="wide"
)

# Functions
@st.cache_data
def get_data():
    return load_data()

# Main app
def main():
    st.title("My Streamlit App")
    
    # Load data
    data = get_data()
    
    # Sidebar
    with st.sidebar:
        st.header("Settings")
        option = st.selectbox("Choose option", ['A', 'B', 'C'])
    
    # Main content
    if option == 'A':
        show_page_a(data)
    elif option == 'B':
        show_page_b(data)
    else:
        show_page_c(data)

def show_page_a(data):
    st.header("Page A")
    st.dataframe(data)

def show_page_b(data):
    st.header("Page B")
    st.line_chart(data)

def show_page_c(data):
    st.header("Page C")
    st.write("Page C content")

if __name__ == "__main__":
    main()

11.2 Performance Optimization

βœ… DO:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 1. Cache expensive operations
@st.cache_data
def load_large_dataset():
    return pd.read_csv('large_file.csv')

# 2. Cache ML models
@st.cache_resource
def load_model():
    return joblib.load('model.pkl')

# 3. Use columns for better layout
col1, col2 = st.columns(2)
with col1:
    st.write("Column 1")
with col2:
    st.write("Column 2")

# 4. Use forms to batch inputs
with st.form("my_form"):
    name = st.text_input("Name")
    age = st.slider("Age", 0, 100)
    submitted = st.form_submit_button("Submit")

# 5. Use session state for persistence
if 'data' not in st.session_state:
    st.session_state.data = expensive_computation()

# 6. Minimize reruns with callbacks
def update_value():
    st.session_state.result = process(st.session_state.input_value)

st.text_input("Input", key="input_value", on_change=update_value)

❌ DON’T:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 1. Don't load data without caching
def load_data():  # Bad - loads on every rerun
    return pd.read_csv('data.csv')

@st.cache_data
def load_data():  # Good - cached
    return pd.read_csv('data.csv')

# 2. Don't use global variables
counter = 0  # Bad - resets on rerun
if st.button("Increment"):
    counter += 1  # Doesn't work!

# Use session state instead
if 'counter' not in st.session_state:
    st.session_state.counter = 0
if st.button("Increment"):
    st.session_state.counter += 1  # Works!

# 3. Don't repeatedly create widgets in loops
for i in range(1000):  # Bad - creates 1000 widgets
    st.write(f"Item {i}")

# Use dataframe instead
df = pd.DataFrame({'Item': [f"Item {i}" for i in range(1000)]})
st.dataframe(df)  # Good - single widget

# 4. Don't ignore wide layout for data apps
st.set_page_config(layout="wide")  # Good for data-heavy apps

# 5. Don't forget to handle errors
try:
    result = risky_operation()
except Exception as e:
    st.error(f"Error: {str(e)}")

11.3 User Experience Tips

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import streamlit as st

# 1. Loading states
with st.spinner("Loading data..."):
    data = load_data()

# 2. Clear feedback
if st.button("Save"):
    save_data()
    st.success("Data saved successfully!")

# 3. Help text
st.text_input("Email", help="Enter your email address")

# 4. Expanders for optional content
with st.expander("Advanced Settings"):
    st.slider("Parameter", 0, 100)

# 5. Empty placeholders for dynamic content
placeholder = st.empty()
for i in range(100):
    placeholder.write(f"Progress: {i}%")
    time.sleep(0.1)
placeholder.success("Complete!")

# 6. Conditional display
if st.checkbox("Show raw data"):
    st.write(df)

# 7. Clear instructions
st.markdown("""
### How to use this app:
1. Upload your data
2. Select parameters
3. Click 'Run Analysis'
""")

Phase 12: Deployment

12.1 Streamlit Community Cloud

Deployment Steps:

  1. Prepare your repository:
1
2
3
4
5
6
myapp/
β”œβ”€β”€ app.py
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ .streamlit/
β”‚   └── config.toml
└── README.md

requirements.txt:

1
2
3
4
streamlit==1.28.0
pandas==2.0.0
numpy==1.24.0
plotly==5.17.0
  1. Push to GitHub

  2. Deploy on Streamlit Cloud:

    • Go to share.streamlit.io
    • Connect GitHub repository
    • Select main file (app.py)
    • Click β€œDeploy”

12.2 Docker Deployment

Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8501

HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health

ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]

docker-compose.yml:

1
2
3
4
5
6
7
8
9
10
11
12
version: '3.8'

services:
  streamlit:
    build: .
    ports:
      - "8501:8501"
    volumes:
      - ./data:/app/data
    environment:
      - STREAMLIT_SERVER_PORT=8501
    restart: unless-stopped

Build and run:

1
2
3
4
5
6
7
8
# Build
docker build -t my-streamlit-app .

# Run
docker run -p 8501:8501 my-streamlit-app

# Or with docker-compose
docker-compose up -d

12.3 Production Configuration

.streamlit/config.toml (production):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[server]
port = 8501
enableCORS = false
enableXsrfProtection = true
maxUploadSize = 200

[browser]
gatherUsageStats = false

[client]
toolbarMode = "minimal"
showErrorDetails = false

[runner]
magicEnabled = true
fastReruns = true

12.4 Environment Variables

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import streamlit as st
import os

# Read from environment
API_KEY = os.getenv('API_KEY')
DATABASE_URL = os.getenv('DATABASE_URL')

# Secrets management (Streamlit Cloud)
# .streamlit/secrets.toml (local, not committed)
# [connections.database]
# url = "postgresql://..."

# Access secrets
db_url = st.secrets["connections"]["database"]["url"]
api_key = st.secrets["api_key"]

Performance Benchmarks

Streamlit vs Traditional Web Frameworks

MetricStreamlitFlask + HTML/JSDjango
Lines of Code (Simple Dashboard)50300+400+
Development TimeHoursDaysDays
Learning CurveGentleSteepSteep
Ideal ForData Apps, PrototypesGeneral Web AppsFull Web Apps
Frontend Skills RequiredNoneHTML/CSS/JSHTML/CSS/JS
Backend ComplexityLowMediumHigh

Caching Impact

Without Caching: $ \text{Total Time} = N_{\text{reruns}} \times T_{\text{computation}} $

With Caching: $ \text{Total Time} = T_{\text{computation}} + (N_{\text{reruns}} - 1) \times T_{\text{cache lookup}} $

Where $T_{\text{cache lookup}} \ll T_{\text{computation}}$

Example:

  • Computation time: 10 seconds
  • Cache lookup: 0.01 seconds
  • 100 reruns

Without cache: $100 \times 10 = 1000$ seconds With cache: $10 + (99 \times 0.01) = 10.99$ seconds

Speedup: $\frac{1000}{10.99} \approx 91\times$


Common Patterns and Solutions

Pattern 1: Multi-Page Navigation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import streamlit as st

def render_page_home():
    st.title("Home")
    if st.button("Go to Dashboard"):
        st.session_state.page = "dashboard"
        st.rerun()

def render_page_dashboard():
    st.title("Dashboard")
    if st.button("Go to Home"):
        st.session_state.page = "home"
        st.rerun()

# Initialize
if 'page' not in st.session_state:
    st.session_state.page = "home"

# Route
if st.session_state.page == "home":
    render_page_home()
else:
    render_page_dashboard()

Pattern 2: Data Pipeline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import streamlit as st
import pandas as pd

st.title("Data Processing Pipeline")

# Step 1: Upload
uploaded_file = st.file_uploader("Upload CSV", type=['csv'])

if uploaded_file:
    # Step 2: Load
    df = pd.read_csv(uploaded_file)
    st.success("File loaded!")
    
    # Step 3: Preview
    with st.expander("Preview Raw Data"):
        st.dataframe(df.head())
    
    # Step 4: Clean
    if st.checkbox("Remove missing values"):
        df = df.dropna()
        st.info(f"Removed missing values. New shape: {df.shape}")
    
    # Step 5: Transform
    if st.checkbox("Normalize numeric columns"):
        numeric_cols = df.select_dtypes(include=['float64', 'int64']).columns
        df[numeric_cols] = (df[numeric_cols] - df[numeric_cols].mean()) / df[numeric_cols].std()
        st.info("Normalized numeric columns")
    
    # Step 6: Export
    csv = df.to_csv(index=False).encode('utf-8')
    st.download_button("Download Processed Data", csv, "processed.csv")

Pattern 3: Dashboard with Filters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import streamlit as st
import pandas as pd
import plotly.express as px

st.set_page_config(layout="wide")

# Load data
@st.cache_data
def load_data():
    return pd.read_csv('sales_data.csv')

df = load_data()

# Sidebar filters
with st.sidebar:
    st.header("Filters")
    date_range = st.date_input("Date Range", [df['date'].min(), df['date'].max()])
    regions = st.multiselect("Regions", df['region'].unique(), default=df['region'].unique())
    
# Apply filters
filtered_df = df[
    (df['date'] >= date_range[0]) &
    (df['date'] <= date_range[1]) &
    (df['region'].isin(regions))
]

# Metrics
col1, col2, col3, col4 = st.columns(4)
col1.metric("Total Sales", f"${filtered_df['sales'].sum():,.0f}")
col2.metric("Total Orders", f"{len(filtered_df):,}")
col3.metric("Avg Order Value", f"${filtered_df['sales'].mean():,.2f}")
col4.metric("Unique Customers", f"{filtered_df['customer_id'].nunique():,}")

# Charts
col1, col2 = st.columns(2)

with col1:
    fig = px.line(filtered_df, x='date', y='sales', title='Sales Over Time')
    st.plotly_chart(fig, use_container_width=True)

with col2:
    fig = px.bar(filtered_df.groupby('region')['sales'].sum().reset_index(), 
                 x='region', y='sales', title='Sales by Region')
    st.plotly_chart(fig, use_container_width=True)

# Data table
st.dataframe(filtered_df, use_container_width=True)

Conclusion

Streamlit has democratized web app development for data scientists and machine learning engineers, eliminating the traditional barriers of frontend development. Its intuitive Python-first approach, combined with powerful caching mechanisms and automatic UI rendering, enables rapid prototyping and deployment of data-driven applications.

Key Takeaways:

  1. Pure Python - No HTML, CSS, or JavaScript required
  2. Reactive by default - Automatic reruns on interaction
  3. Caching is essential - Use @st.cache_data and @st.cache_resource
  4. Session state - Persist data across reruns
  5. Layout flexibility - Columns, tabs, expanders, sidebar
  6. Quick prototyping - Hours instead of days
  7. Easy deployment - Streamlit Community Cloud, Docker, cloud platforms
  8. Data-focused - Built for DataFrames, charts, and ML models
  9. Component ecosystem - Extend with custom components
  10. Production-ready - With proper caching and optimization

By following the practices outlined in this guide and understanding Streamlit’s execution model, you’ll be equipped to rapidly build, iterate, and deploy interactive data applications that transform insights into impact.


References

Streamlit Official Documentation

Streamlit API Reference

Streamlit Getting Started Guide

Streamlit Caching Guide

Streamlit Session State

Multi-page Apps

Streamlit Community Cloud

Streamlit Components

Streamlit GitHub Repository

Streamlit Blog


Last Updated: November 12, 2025

This post is licensed under CC BY 4.0 by the author.