Installs into .claude/skills of the current project.
Are you the author of Building Streamlit Multipage Apps?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/mouadja02-building-streamlit-multipage-apps)
---
name: building-streamlit-multipage-apps
description: Use when a Streamlit app needs more than one page — the pages directory, st.navigation and st.Page, and sharing state across pages.
license: Apache-2.0
source: "https://github.com/streamlit/agent-skills"
attribution: "streamlit/agent-skills by Streamlit"
---
> **Attribution:** Sourced from [streamlit/agent-skills](https://github.com/streamlit/agent-skills) by [Streamlit](https://streamlit.io).
# 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
```python
# 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:**
```python
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:**
```python
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:
```python
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
```python
# 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:
```python
# 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:
```python
# 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:
```python
# 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:
```python
if st.button("Go to Settings"):
st.switch_page("app_pages/settings.py")
```
Create navigation links with `st.page_link`:
```python
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:
```python
# 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:
```python
# 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
- [Multipage apps docs](https://docs.streamlit.io/develop/concepts/multipage-apps)
- [st.navigation](https://docs.streamlit.io/develop/api-reference/navigation/st.navigation)
- [st.Page](https://docs.streamlit.io/develop/api-reference/navigation/st.page)
- [st.session_state](https://docs.streamlit.io/develop/api-reference/caching-and-state/st.session_state)
- [st.switch_page](https://docs.streamlit.io/develop/api-reference/navigation/st.switch_page)
- [st.page_link](https://docs.streamlit.io/develop/api-reference/widgets/st.page_link)