If anyone uses Streamlit for prototype applications, dashboards, testing ideas, etc., you might have had issues with the outdated docs in Cursor @docs, as with many other docs. Pydantic AI can also extract and rewrite documentation through the same process as discussed above. I added this Streamlit doc for the December release 1.41, and it seems to work very well on my end. It is a shorter version based on the release updates and quick reference.
Or see it here; please comment on it if it works for you. If you have suggestions I should add in the Pydantic AI script, FYI it is intended to be less than 30,000 tokens.
Click to expand the Streamlit 1.41 docs
Streamlit API Cheat Sheet
This is a comprehensive summary of the docs for the latest version of Streamlit, v1.41.0.
Release Notes for Streamlit v1.41.0
Streamlit v1.41.0 introduces several new features, enhancements, and bug fixes to improve the developer experience and expand the capabilities of your Streamlit applications.
New Features
-
Native Support for Async Functions: Streamlit now natively supports asynchronous functions, allowing for more efficient handling of I/O-bound operations.
import streamlit as st
import asyncio
async def fetch_data():
await asyncio.sleep(1)
return "Data fetched!"
async def main():
data = await fetch_data()
st.write(data)
asyncio.run(main())
-
Enhanced Widget Customization: New parameters for widgets enable deeper customization, including theming and styling options.
st.button("Submit", key="submit_btn", disabled=False, type="tertiary", style={"color": "white", "backgroundColor": "#4CAF50"})
-
Improved Caching Mechanism: The caching system now supports versioning and provides more granular control over cache invalidation.
@st.cache_data(version=2)
def load_data(path):
return pd.read_csv(path)
-
Expanded Media Support: Additional media formats are now supported, including SVG images and WebM videos.
from pathlib import Path
image_path = Path("./diagram.svg")
video_path = Path("./animation.webm")
st.image(image_path)
st.video(video_path)
-
Multi-Page App Enhancements: Improved navigation and state management across multiple pages in a Streamlit app.
st.set_page_config(page_title="My App", layout="wide", theme={"primaryColor": "#F63366"})
-
Pathlib Support: Streamlit supports pathlib.Path
objects everywhere you can use a string path.
from pathlib import Path
data_path = Path("data/my_data.csv")
st.dataframe(pd.read_csv(data_path))
-
Date and Time Inputs Accept ISO Strings: [st.date_input
] and [st.time_input
] accept ISO formatted strings for initial values.
st.date_input("Select a date", value="2024-12-13")
st.time_input("Select a time", value="14:30:00")
-
Async Generators in st.write_stream: [st.write_stream
] accepts async generators, which it converts internally to sync generators.
import streamlit as st
import asyncio
async def async_generator():
for i in range(5):
await asyncio.sleep(1)
yield f"Message {i}"
async def main():
async for message in async_generator():
st.write_stream(message)
asyncio.run(main())
Enhancements
- Performance Optimizations: Reduced load times and improved rendering performance for large datasets and complex layouts.
- Accessibility Improvements: Enhanced support for screen readers and keyboard navigation to make apps more accessible.
- Better Error Messages: More informative and user-friendly error messages to aid in debugging.
- Theming Enhancements: Text and background color in Markdown can use the “primary” color from the
theme.primaryColor
configuration option.
Bug Fixes
- Fixed issues related to widget state persistence across reruns.
- Resolved compatibility problems with Python 3.13 and dropped support for Python 3.8.
- Addressed layout inconsistencies in the sidebar and main content areas.
- Multiple other bug fixes as detailed in the release notes.
Table of Contents
- Installation & Import
- Command Line Interface
- Magic Commands
- Display Text
- Display Data
- Display Media
- Display Charts
- Sidebar Elements
- Layout Management
- Tabs
- Expandable Containers
- Control Flow
- Interactive Widgets
- Chat-Based Apps
- Data Mutation
- Display Code
- Placeholders, Help, and Options
- Data Source Connections
- Performance Optimization
- Progress and Status Indicators
- User Personalization
- Advanced Features
Install & Import
Install Streamlit
pip install streamlit
Run Your First App
streamlit run first_app.py
Import Convention
import streamlit as st
Pre-release Features
pip uninstall streamlit
pip install streamlit-nightly --upgrade
Learn more about experimental features
Command Line
Streamlit CLI Commands
streamlit --help # Show all commands
streamlit run your_script.py # Run a Streamlit app
streamlit hello # Launch the Streamlit hello app
streamlit config show # Show current config
streamlit cache clear # Clear cached data
streamlit docs # Open Streamlit documentation
streamlit --version # Show Streamlit version
Magic Commands
Magic commands implicitly call st.write()
. These commands allow you to write Markdown, display variables, and more without explicitly calling st.write()
.
# Implicitly calls st.write()
"_This_ is some **Markdown**"
my_variable
"dataframe:", my_data_frame
Magic Commands Enhancements
# Display dynamic markdown with variables
name = "Alice"
st.markdown(f"Hello, **{name}**!")
# Conditional rendering
if condition:
"Condition is True"
else:
"Condition is False"
Display Text
Streamlit offers various functions to display different types of text and formatted content:
st.write("Most objects") # Display various objects
st.write(["st", "is <", 3]) # Display lists
st.write_stream(my_generator) # Stream data
st.write_stream(my_llm_stream) # Stream from language models
st.text("Fixed width text") # Display fixed-width text
st.markdown("_Markdown_", style={"color": st.theme.primaryColor}) # Render Markdown with primary color
st.latex(r""" e^{i\pi} + 1 = 0 """) # Render LaTeX
st.title("My Title") # Large header
st.header("My Header") # Medium header
st.subheader("My Sub") # Smaller header
st.code("for i in range(8): foo()") # Display code snippets
st.html("<p>Hi!</p>") # Render raw HTML
Display Data
st.dataframe
Display a dataframe as an interactive table. This command works with a wide variety of collection-like and dataframe-like object types.
st.dataframe(my_dataframe) # Interactive DataFrame, now supports more dataframe formats including pathlib.Path
Function Signature
st.dataframe(
data=None,
width=None,
height=None,
*,
use_container_width=False,
hide_index=None,
column_order=None,
column_config=None,
key=None,
on_select="ignore",
selection_mode="multi-row"
)
Parameters
- data (dataframe-like, collection-like, or None): The data to display. Supports pandas, Polars, Snowflake, and more. If
data
is None
, an empty table is rendered.
- width (int or None): Desired width in pixels. If
None
, fits contents up to the parent container’s width.
- height (int or None): Desired height in pixels. Defaults to showing at most ten rows with vertical scrolling.
- use_container_width (bool): If
True
, overrides width
to match the parent container’s width.
- hide_index (bool or None): If
True
, hides the index column(s). Automatically determined if None
.
- column_order (Iterable of str or None): Ordered list of columns to display.
None
displays all columns in their original order.
- column_config (dict or None): Customizes column display, such as names, visibility, types, widths, and formats. Use
_index
to configure index columns.
- key (str): Unique identifier for the dataframe in Session State.
- on_select (“ignore” or “rerun” or callable): Defines response to user selection events.
"ignore"
: No interaction.
"rerun"
: App reruns upon selection.
callable
: Executes a callback before rerun.
- selection_mode (“single-row”, “multi-row”, “single-column”, “multi-column”, or Iterable): Types of selections allowed.
Returns
- element or dict: Returns an internal placeholder for adding rows if
on_select="ignore"
. Otherwise, returns a dictionary-like object with selection data.
Examples
# Basic usage
st.dataframe(pd.DataFrame({
'A': [1, 2, 3],
'B': ['a', 'b', 'c']
}))
# Customizing columns
st.dataframe(
df,
column_order=["B", "A"],
column_config={
"A": st.column_config.NumberColumn("Numbers", format="$%d"),
"B": st.column_config.StringColumn("Letters")
}
)
# Handling selections
def handle_selection(selection):
st.write("Selected:", selection)
st.dataframe(
df,
on_select=handle_selection,
selection_mode="multi-row"
)
st.form
Create a form that batches elements together with a “Submit” button. Forms are containers that group widgets and contain a Submit button. When submitted, all widget values inside the form are sent to Streamlit in a batch.
with st.form(key="my_form"):
username = st.text_input("Username")
password = st.text_input("Password", type="password")
st.form_submit_button("Login")
Function Signature
st.form(
key,
clear_on_submit=False,
*,
enter_to_submit=True,
border=True
)
Parameters
- key (str): Unique identifier for the form.
- clear_on_submit (bool): If
True
, resets widgets to default values after submission. Defaults to False
.
- enter_to_submit (bool): If
True
, pressing Enter submits the form. Defaults to True
.
- border (bool): If
True
, shows a border around the form. Defaults to True
.
Constraints
- Every form must contain a
st.form_submit_button
.
st.button
and st.download_button
cannot be added to a form.
- Forms cannot be nested within other forms.
- Only
st.form_submit_button
can have a callback within a form.
Examples
# Basic form
with st.form(key="login_form"):
username = st.text_input("Username")
password = st.text_input("Password", type="password")
submit = st.form_submit_button("Login")
if submit:
st.write(f"Welcome, {username}!")
# Form with clearing on submit
with st.form(key="search_form", clear_on_submit=True):
query = st.text_input("Search Query")
submit = st.form_submit_button("Search")
if submit:
results = perform_search(query)
st.write(results)
# Form with custom submission behavior
def handle_submit():
st.write("Form submitted!")
with st.form(key="custom_form"):
data = st.text_area("Enter data")
submit = st.form_submit_button("Submit", on_click=handle_submit)
Display Media
from pathlib import Path
image_path = Path("./header.png")
audio_path = Path("./audio.mp3")
video_path = Path("./video.webm")
st.image(image_path) # Display an image, supports pathlib.Path
st.audio(audio_path) # Play audio data, supports autoplay and correct time zones
st.video(video_path, autoplay=True, muted=True) # Play video data with autoplay and mute
st.logo("logo.jpg", size=(100, 100)) # Display a logo image with adjustable size
Display Charts
# Built-in charts
st.area_chart(df, use_container_width=True, height=400) # Area chart with container width and height
st.bar_chart(df, horizontal=True, border=True) # Bar chart horizontal with optional border
st.line_chart(df, use_container_width=True) # Line chart with tooltips on hover
st.map(df) # Geospatial data, now supports freezing columns with configuration
st.scatter_chart(df)
# External libraries
st.altair_chart(chart)
st.bokeh_chart(fig)
st.graphviz_chart(fig)
st.plotly_chart(fig, config={"displayModeBar": False}) # Plotly charts with tooltips
st.pydeck_chart(chart, height=600)
st.pyplot(fig) # Matplotlib figures
st.vega_lite_chart(df, spec)
# Interactive charts with user selections
event = st.plotly_chart(df, on_select="rerun")
event = st.altair_chart(chart, on_select="rerun")
event = st.vega_lite_chart(df, spec, on_select="rerun")
Add Elements to Sidebar
# Directly add to sidebar
a = st.sidebar.radio("Select one:", [1, 2])
# Using "with" notation
with st.sidebar:
st.radio("Select one:", [1, 2])
Columns
# Two equal columns with optional border
col1, col2 = st.columns(2)
col1.write("This is column 1")
col2.write("This is column 2")
# Three columns with different widths and optional border
col1, col2, col3 = st.columns([3, 1, 1], border=True) # col1 is larger
# Bottom-aligned columns
col1, col2 = st.columns(2, vertical_alignment="bottom")
# Using "with" notation
with col1:
st.radio("Select one:", [1, 2])
# Freezing columns with column configuration
st.column_config(col1, frozen=True)
Tabs
# Create tabs
tab1, tab2 = st.tabs(["Tab 1", "Tab2"])
tab1.write("This is tab 1")
tab2.write("This is tab 2")
# Using "with" notation
with tab1:
st.radio("Select one:", [1, 2])
Expandable Containers
expand = st.expander("My label", icon=":material/info:")
expand.write("Inside the expander.")
pop = st.popover("Button label")
pop.checkbox("Show all")
# Using "with" notation
with expand:
st.radio("Select one:", [1, 2])
Control Flow
# Stop execution immediately
st.stop()
# Rerun script immediately
st.rerun()
# Navigate to another page
st.switch_page("pages/my_page.py")
# Define a navigation widget in your entrypoint file
pg = st.navigation(
st.Page("page1.py", title="Home", url_path="home", default=True),
st.Page("page2.py", title="Preferences", url_path="settings")
)
pg.run()
# Group multiple widgets in a form
with st.form(key="my_form"):
username = st.text_input("Username")
password = st.text_input("Password", type="password")
st.form_submit_button("Login")
# Define a dialog function
@st.dialog("Welcome!")
def modal_dialog():
st.write("Hello")
modal_dialog()
# Define a fragment for reusable UI components
@st.fragment
def fragment_function():
df = get_data()
st.line_chart(df)
st.button("Update")
fragment_function()
# Using st.write_stream with async generators
import asyncio
async def async_gen():
for i in range(5):
await asyncio.sleep(1)
yield f"Message {i}"
st.write_stream(async_gen())
Display Interactive Widgets
# Buttons and Actions
st.button("Click me", type="tertiary")
st.download_button("Download file", data)
st.link_button("Go to gallery", url)
st.page_link("app.py", label="Home", icon=":material/home:")
# Data Editing
st.data_editor("Edit data", data, column_config={"frozen": True})
# Selection Widgets
st.checkbox("I agree")
st.feedback("thumbs")
st.pills("Tags", ["Sports", "Politics"])
st.radio("Pick one", ["cats", "dogs"])
st.segmented_control("Filter", ["Open", "Closed"])
st.toggle("Enable")
st.selectbox("Pick one", ["cats", "dogs"])
st.multiselect("Buy", ["milk", "apples", "potatoes"])
# Input Widgets
st.slider("Pick a number", 0, 100)
st.select_slider("Pick a size", ["S", "M", "L"])
st.text_input("First name")
st.number_input("Pick a number", 0, 10)
st.text_area("Text to translate")
st.date_input("Your birthday", value="1990-01-01")
st.time_input("Meeting time", value="09:00:00")
st.file_uploader("Upload a CSV", type=["csv"])
st.audio_input("Record a voice message")
st.camera_input("Take a picture")
st.color_picker("Pick a color")
# Using widget values in variables
for i in range(int(st.number_input("Num:"))):
foo()
if st.sidebar.selectbox("I:", ["f"]) == "f":
b()
my_slider_val = st.slider("Quinn Mallory", 1, 88)
st.write(my_slider_val)
# Disable widgets to remove interactivity
st.slider("Pick a number", 0, 100, disabled=True)
Build Chat-Based Apps
# Insert a chat message container
with st.chat_message("user"):
st.write("Hello 👋")
st.line_chart(np.random.randn(30, 3))
# Display a chat input widget at the bottom of the app
st.chat_input("Say something")
# Display a chat input widget inline
with st.container():
st.chat_input("Say something")
Learn how to Build a basic LLM chat app
Mutate Data
# Add rows to a dataframe after showing it
element = st.dataframe(df1)
element.add_rows(df2)
# Add rows to a chart after showing it
element = st.line_chart(df1)
element.add_rows(df2)
Display Code
with st.echo():
st.write("Code will be executed and printed")
import pandas as pd
df = pd.DataFrame({"A": [1, 2, 3]})
st.write(df)
Placeholders, Help, and Options
# Replace any single element
element = st.empty()
element.line_chart(...)
element.text_input(...) # Replaces the previous element
# Insert out of order using containers
elements = st.container()
elements.line_chart(...)
st.write("Hello")
elements.text_input(...) # Appears above "Hello"
# Display help and access options
st.help(pandas.DataFrame) # Display help for pandas DataFrame
st.get_option(key) # Get a Streamlit config option
st.set_option(key, value) # Set a Streamlit config option
# Configure page settings
st.set_page_config(layout="wide", page_title="My App", page_icon=":smile:", theme={
"primaryColor": "#F63366",
"backgroundColor": "#FFFFFF",
"secondaryBackgroundColor": "#F0F2F6",
"textColor": "#262730",
"font": "sans serif"
})
# Manage query parameters
st.query_params[key]
st.query_params.from_dict(params_dict)
st.query_params.get_all(key)
st.query_params.clear()
# Render raw HTML
st.html("<p>Hi!</p>")
Connect to Data Sources
# Define a connection to a SQL database
st.connection("pets_db", type="sql")
conn = st.connection("sql")
conn = st.connection("snowflake")
# Custom connection class
class MyConnection(BaseConnection[myconn.MyConnection]):
def _connect(self, **kwargs) -> MyConnection:
return myconn.connect(**self._secrets, **kwargs)
def query(self, query):
return self._instance.query(query)
# Using the connection
my_conn = MyConnection()
result = my_conn.query("SELECT * FROM pets")
st.write(result)
Optimize Performance
Cache Data Objects
@st.cache_data
def foo(bar):
# Perform expensive computation or data retrieval
return data
# Execute foo
d1 = foo(ref1)
# Retrieve cached result
d2 = foo(ref1) # d1 == d2
# Different argument triggers recomputation
d3 = foo(ref2)
# Clear specific cache entry
foo.clear(ref1)
# Clear all cache entries for the function
foo.clear()
# Clear all cached data
st.cache_data.clear()
Cache Global Resources
@st.cache_resource
def foo(bar):
# Create and return a resource
return session
# Execute foo
s1 = foo(ref1)
# Retrieve cached resource
s2 = foo(ref1) # s1 == s2
# Different argument triggers recomputation
s3 = foo(ref2)
# Clear specific cache entry
foo.clear(ref1)
# Clear all cache entries for the function
foo.clear()
# Clear all cached resources
st.cache_resource.clear()
Display Progress and Status
import time
# Show a spinner during a process
with st.spinner(text="In progress"):
time.sleep(3)
st.success("Done")
# Show and update a progress bar
bar = st.progress(50)
time.sleep(3)
bar.progress(100)
# Show and update a status message
with st.status("Authenticating...") as s:
time.sleep(2)
st.write("Some long response.")
s.update(label="Response")
# Visual effects
st.balloons() # Display balloons animation
st.snow() # Display snow animation
st.toast("Warming up...") # Show a toast message
# Display different types of messages
st.error("Error message")
st.warning("Warning message")
st.info("Info message")
st.success("Success message")
st.exception(e) # Display exception details
Personalize Apps for Users
# Show different content based on the user's email address
if st.experimental_user.email == "jane@examples.com":
display_jane_content()
elif st.experimental_user.email == "adam@example.com":
display_adam_content()
else:
st.write("Please contact us to get access!")
# Access cookies and headers
cookies = st.context.cookies
headers = st.context.headers
Advanced Features
Magic Commands Enhancements
# Display dynamic markdown with variables
name = "Alice"
st.markdown(f"Hello, **{name}**!")
# Conditional rendering
if condition:
"Condition is True"
else:
"Condition is False"
Theming and Styling
# Set theme in config
st.set_page_config(
page_title="Themed App",
layout="centered",
initial_sidebar_state="expanded",
theme={
"primaryColor": "#F63366",
"backgroundColor": "#FFFFFF",
"secondaryBackgroundColor": "#F0F2F6",
"textColor": "#262730",
"font": "sans serif"
}
)
# Inject custom CSS
st.markdown(
"""
<style>
.big-font {
font-size:50px !important;
}
</style>
""",
unsafe_allow_html=True
)
st.markdown('<p class="big-font">This is a big font text</p>', unsafe_allow_html=True)
Session State Management
# Initialize session state
if 'count' not in st.session_state:
st.session_state.count = 0
# Increment counter
def increment():
st.session_state.count += 1
st.button("Increment", on_click=increment)
st.write(f"Count: {st.session_state.count}")
# Using session state in widgets
selected_option = st.selectbox("Choose", ["Option 1", "Option 2"], key="select")
st.write(f"Selected: {st.session_state.select}")
Custom Components
import streamlit.components.v1 as components
# Define a custom component
my_component = components.declare_component("my_component", path="frontend/build")
# Use the custom component
output = my_component(key="unique_key", some_prop="value")
st.write(output)
Internationalization (i18n)
from gettext import gettext as _
# Set language
language = st.selectbox("Select language", ["en", "es", "fr"])
# Load translations
translations = {
"en": {"greet": "Hello"},
"es": {"greet": "Hola"},
"fr": {"greet": "Bonjour"},
}
st.write(translations[language]["greet"])
Additional Resources
Tips & Best Practices
- Use Caching Wisely: Cache data and resources to improve performance but be mindful of cache invalidation to ensure data freshness.
- Optimize Layouts: Utilize columns, tabs, and sidebars to create intuitive and organized interfaces.
- Leverage Session State: Maintain user interactions and data across app reruns using
st.session_state
.
- Enhance Accessibility: Ensure your app is accessible by following accessibility best practices and utilizing Streamlit’s accessibility features.
- Secure Sensitive Data: Handle user data and secrets securely using Streamlit’s secrets management.
Troubleshooting
- App Not Updating: Ensure that widgets are correctly placed and that caching isn’t preventing updates. Use
st.experimental_rerun()
if necessary.
- Performance Issues: Profile your app to identify bottlenecks. Optimize data processing and leverage caching where appropriate.
- Widget State Loss: Use unique keys for widgets to maintain state across reruns and interactions.
- Layout Problems: Use Streamlit’s layout primitives like columns and containers to manage complex layouts effectively.
Useful Links