BU

building-streamlit-multipage-apps

Standardizes the structure and navigation of multi-page Streamlit apps using `app_pages/` and shared session state.

Install

mkdir -p .claude/skills/building-streamlit-multipage-apps && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15353" && unzip -o skill.zip -d .claude/skills/building-streamlit-multipage-apps && rm skill.zip

Installs to .claude/skills/building-streamlit-multipage-apps

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Building multi-page Streamlit apps. Use when creating apps with multiple pages, setting up navigation, or managing state across pages.
134 chars✓ has a “when” trigger
Beginner

Key capabilities

  • Structure multi-page Streamlit applications
  • Set up navigation for multiple pages
  • Manage global session state across pages
  • Implement page-specific state
  • Share UI elements across all pages
  • Enable programmatic navigation between pages

How it works

This skill provides a blueprint for structuring Streamlit applications with multiple pages, defining navigation, and managing global and page-specific state.

Inputs & outputs

You give it
a Streamlit application with multiple desired pages
You get back
a structured multi-page Streamlit application with navigation and state management

When to use building-streamlit-multipage-apps

  • Structuring a multi-page app
  • Managing state in streamlit
  • Creating a navigation menu

About this skill

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.py
import streamlit as st

# Initialize global state (shared across pages)
if "api_client" not in 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="Settings", icon=":material/settings:"),
])

# App-level UI runs before page content
# Useful for shared elements like titles
st.title(f"{page.icon} {page.title}")

page.run()

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.

Many pages or nested sections → Sidebar:

page = st.navigation({
    "Main": [
        st.Page("app_pages/home.py", title="Home"),
        st.Page("app_pages/analytics.py", title="Analytics"),
    ],
    "Admin": [
        st.Page("app_pages/settings.py", title="Settings"),
        st.Page("app_pages/users.py", title="Users"),
    ],
}, position="sidebar")

Mixed: Some pages ungrouped:

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:

page = st.navigation({
    "": [
        st.Page("app_pages/home.py", title="Home"),
        st.Page("app_pages/about.py", title="About"),
    ],
    "Analytics": [
        st.Page("app_pages/dashboard.py", title="Dashboard"),
        st.Page("app_pages/reports.py", title="Reports"),
    ],
}, position="top")

Page modules

# app_pages/analytics.py
import 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:

# streamlit_app.py
st.session_state.api = init_client()
st.session_state.user = get_user()
st.session_state.settings = load_settings()

Tip: Use st.session_state.setdefault("key", default_value) to initialize values only if they don't exist.

Why main module (for global state):

  • Runs before every page
  • Ensures state is initialized
  • Single source of truth

Page-specific state

Use prefixed keys for page-specific state:

# app_pages/analytics.py
if "analytics_date_range" not in st.session_state:
    st.session_state.analytics_date_range = default_range()

Share elements between pages

Put shared UI in the entrypoint (before page.run()) so it appears on all pages:

# streamlit_app.py
import streamlit as st

pages = [...]
page = st.navigation(pages)

# Shared title
st.title(f"{page.icon} {page.title}")

# Shared sidebar widgets
with st.sidebar:
    st.selectbox("Theme", ["Light", "Dark"])

page.run()

Programmatic navigation

Navigate to another page programmatically:

if st.button("Go to Settings"):
    st.switch_page("app_pages/settings.py")

Create navigation links with st.page_link:

st.page_link("app_pages/home.py", label="Home", icon=":material/home:")
st.page_link("https://example.com", label="External", icon=":material/open_in_new:")

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.py
import 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()

Common conditions for showing/hiding pages:

  • st.user.is_logged_in for authenticated users
  • st.session_state flags (roles, permissions, feature flags)
  • Environment variables or secrets
  • Time-based access (e.g., beta features)

Imports from pages

When importing from page files in app_pages/, always import from the root directory perspective:

# app_pages/dashboard.py - GOOD
from utils.data import load_sales_data

# app_pages/dashboard.py - BAD (don't use relative imports)
from ..utils.data import load_sales_data

References

When not to use it

  • When building a single-page Streamlit application
  • When using Streamlit's old auto-discovery API for pages
  • When the goal is to recreate the navigation bar with st.page_link

Limitations

  • The page directory must be named app_pages/
  • Global state should be initialized in the main module
  • Page-specific state should use prefixed keys

How it compares

This skill offers a specific, recommended directory structure and navigation setup for Streamlit multi-page apps, avoiding common conflicts and state issues, unlike ad-hoc page creation.

Compared to similar skills

building-streamlit-multipage-apps side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
building-streamlit-multipage-apps (this skill)04moNo flagsBeginner
streamlit869moNo flagsIntermediate
dashboard-build62moReviewAdvanced
developing-with-streamlit04moNo flagsBeginner

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry