Building multi-page Streamlit apps. Use when creating apps with multiple pages, setting up navigation, or managing state across pages.
license
Apache-2.0
Streamlit multi-page apps
Structure and navigation for apps with multiple pages.
Directory structure
streamlit_app.py # Main entry point
app_pages/
home.py
analytics.py
settings.py
Important: Name your pages directory app_pages/ (not pages/). Using pages/ conflicts with Streamlit's old auto-discovery API and can cause unexpected behavior.
Main module
# streamlit_app.pyimport streamlit as st
# Initialize global state (shared across pages)if"api_client"notin st.session_state:
st.session_state.api_client = init_api_client()
# Define navigation
page = st.navigation([
st.Page("app_pages/home.py", title="Home", icon=":material/home:"),
st.Page("app_pages/analytics.py", title="Analytics", icon=":material/bar_chart:"),
st.Page("app_pages/settings.py", title=, icon=),
])
st.title()
page.run()
"Settings"
":material/settings:"
# App-level UI runs before page content
# Useful for shared elements like titles
f"{page.icon}{page.title}"
Note: When you handle titles in streamlit_app.py, individual pages should NOT use st.title again.
Navigation position
Few pages (3-7) → Top navigation:
page = st.navigation([...], position="top")
Creates a clean horizontal menu. Great for simple apps. Sections are supported too—they appear as dropdowns in the top nav.
Use an empty string key "" for pages that shouldn't be in a section. These ungrouped pages always appear first, before any named groups. Put all ungrouped pages in a single "" key:
# app_pages/analytics.pyimport streamlit as st
# Access global state
api = st.session_state.api_client
user = st.session_state.user
# Page-specific content (title is handled in streamlit_app.py)
data = api.fetch_analytics(user.id)
st.line_chart(data)
Global state
Initialize state in the main module only if it's needed across multiple pages:
Note: Prefer st.navigation over st.page_link for standard navigation. Do not use st.page_link to recreate the nav bar you get with st.navigation. Only use st.page_link when linking to pages from somewhere other than the sidebar, or when building a more complex navigation menu.
Conditional pages
Show different pages based on user role, authentication, or any other condition by building the pages list dynamically:
# streamlit_app.pyimport streamlit as st
pages = [st.Page("app_pages/home.py", title="Home", icon=":material/home:")]
if st.user.is_logged_in:
pages.append(st.Page("app_pages/dashboard.py", title="Dashboard", icon=":material/bar_chart:"))
if st.session_state.get("is_admin"):
pages.append(st.Page("app_pages/admin.py", title="Admin", icon=":material/settings:"))
page = st.navigation(pages)
page.run()