Master Code
On The Go

Learn. Practice. Build.

Home › How-To › Streamlit Multi-Page Apps

Streamlit Guide

Streamlit Multi-Page Apps

Turn a single-file script into a full app with navigation, pages, and shared state.

Last updated: October 2026 · Tested with Streamlit 1.40+

Quick answer: Create a pages/ directory next to your app.py. Add one .py file per page. Streamlit automatically adds them to the sidebar navigation. Share state with st.session_state. For full control, use st.navigation() and st.Page().

1

The Folder Structure

The simplest multi-page app has this layout:

my-app/
├── app.py                 # Home page
├── pages/
│   ├── 1_Dashboard.py
│   ├── 2_Settings.py
│   └── 3_Reports.py
├── requirements.txt
└── .streamlit/
    └── secrets.toml

Key rules:

  • The pages/ folder must be next to app.py.
  • Number prefixes (1_, 2_) control the order in the sidebar.
  • Underscores in filenames become spaces in the sidebar.
  • Emojis in filenames become page icons.
2

Your Home Page (app.py)

import streamlit as st

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

st.title("🏠 Home")
st.write("Welcome to my multi-page Streamlit app.")

st.page_link("pages/1_Dashboard.py", label="Go to Dashboard", icon="📊")
st.page_link("pages/2_Settings.py", label="Open Settings", icon="⚙️")

The st.page_link creates internal navigation buttons — great for landing pages.

3

Your First Sub-Page

Create pages/1_Dashboard.py:

import streamlit as st
import pandas as pd
import numpy as np

st.set_page_config(page_title="Dashboard", page_icon="📊")

st.title("📊 Dashboard")
st.write("This is the dashboard page.")

# Sample data
df = pd.DataFrame(
    np.random.randn(20, 3),
    columns=["Sales", "Users", "Revenue"]
)

st.line_chart(df)
st.dataframe(df)

That's it. Streamlit auto-detects the page and adds it to the sidebar.

4

Sharing State Across Pages

Use st.session_state to keep data between pages:

# pages/1_Dashboard.py
if "user" not in st.session_state:
    st.session_state.user = {"name": "Alice"}

st.write(f"Welcome, {st.session_state.user['name']}")

# pages/2_Settings.py
st.write(f"Editing settings for {st.session_state.user['name']}")

if st.button("Rename to Bob"):
    st.session_state.user["name"] = "Bob"
    st.success("Updated!")

💡 Key insight: Session state is per-user, per-session. Each browser tab that opens your app has its own independent state. This is exactly what you want.

5

Programmatic Navigation with st.navigation

For full control over the sidebar — grouping, hiding pages based on login, custom order — use st.navigation:

import streamlit as st

st.set_page_config(page_title="My App")

# Define pages programmatically
home = st.Page("app_pages/home.py", title="Home", icon="🏠", default=True)
dash = st.Page("app_pages/dashboard.py", title="Dashboard", icon="📊")
admin = st.Page("app_pages/admin.py", title="Admin", icon="🔒")

# Group them
pages = {
    "Main": [home, dash],
    "Admin": [admin] if st.session_state.get("is_admin") else [],
}

# Run navigation
pg = st.navigation(pages)
pg.run()

The advantage: you control visibility based on authentication. Non-admin users never see the Admin page.

Don't mix approaches: Use either the pages/ directory or st.navigation — not both. Mixing them leads to duplicated pages in the sidebar.

6

Real-World Example: AI Chatbot with Pages

Here's how to structure a chatbot with a chat page, history page, and settings:

ai-app/
├── app.py                    # Home / landing
├── pages/
│   ├── 1_Chat.py            # Chat interface
│   ├── 2_History.py         # Saved conversations
│   └── 3_Settings.py        # API keys, model picker
└── utils/
    ├── openai_client.py
    └── storage.py

Each page imports from utils/, keeping the code DRY. The chat state lives in st.session_state.

👉 Read our Streamlit AI Chatbot Tutorial.

🎨 Sidebar Navigation Customization

What you want How to do it
Icons on pages Add emoji to filename: 1_📊_Dashboard.py
Custom page title st.set_page_config(page_title="...")
Reorder pages Prefix filenames with numbers
Hide a page Prefix with underscore: _hidden.py
Group pages Use st.navigation() with a dict
Auth-based visibility Filter pages conditionally with st.navigation()

🛡️ Best Practices

Common mistake: Putting st.set_page_config() after other st commands. It must be the first Streamlit call or it throws an error.

❓ Frequently Asked Questions

How do I create a multi-page app in Streamlit?

Create a pages/ directory next to your main app.py. Add one .py file per page. Streamlit automatically adds them to the sidebar navigation. Each page file is run like a normal Streamlit script.

Does Streamlit support multiple pages?

Yes. Streamlit has built-in multi-page support via the pages/ directory. Since Streamlit 1.36, you can also use st.navigation and st.Page for programmatic control.

How do I share state between Streamlit pages?

Use st.session_state. Data stored there persists across page navigation within the same session. You cannot share state between different users — each user has their own session.

How do I customize the Streamlit sidebar navigation?

Use st.navigation() to define pages programmatically. This lets you control page titles, icons, grouping, and order. It also lets you hide pages based on authentication.

Can I have a landing page in Streamlit?

Yes. Your main app.py acts as the default landing page. Users land on it when they first visit your app. Use st.page_link to link to other pages from the landing page.

🎯 What's Next?

← All How-To Guides