Skip to content

Multipage Apps

Streamlit provides two built-in mechanisms for creating multipage apps.

  • The simplest method is to use a pages/ directory.
  • However, the preferred and more customizable method is to use st.navigation.

pages/ directory

For a quick and easy way to add multiple pages to your Streamlit app, simply create a pages/ directory alongside your main entry point file (the file you pass to streamlit run). Each Python file in the pages/ directory will automatically become a new page in your app. Streamlit will generate the page labels and URLs based on the file names and display a navigation menu at the top of the app's sidebar.

shell
your_working_directory/
├── pages/
   ├── a_page.py
   └── another_page.py
└── your_homepage.py

If you want more control over the navigation, you can disable the default navigation by setting client.showSidebarNavigation = false in your configuration. Then, you can manually build a custom menu using st.page_link, which allows you to modify page labels and icons, though the URLs will remain unchanged.

st.Page and st.navigation

If you want maximum flexibility in defining your multipage app, use st.Page and st.navigation. With st.Page you can declare any Python file or Callable (any object that can be invoked like a function) as a page in your app. You can also define common elements in your entry point file, which acts as a shared "frame" for all pages.

You must include st.navigation in your entry point file to configure your app's navigation menu. This also allows the entry point file to act as the router, managing navigation between pages.

NOTE

You can call st.navigation only once per app run.

If you call st.navigation in your app (in any session), Streamlit will disregard pages/ directory, giving priority to st.navigation.

shell
your-repository/
├── page_1.py
├── page_2.py
└── streamlit_app.py
python
# Entry point file (streamlit_app.py)
import streamlit as st

# You can define shared content here that will be present on every page
st.title("Welcome to My Multipage Streamlit App!")
st.write("This is a shared element that will appear across all pages.")

pg = st.navigation([st.Page("page_1.py"), st.Page("page_2.py")])
# st.navigation returns the selected page and its .run() method should be invoked
pg.run()
python
# page_1.py
import streamlit as st

st.write("This is the page_1 content!")

Define pages using st.Page

st.Page allows you to define a page in your app. The main argument specifies the page source, which can be a Python file or a function.

Additionally, st.Page lets you configure page title, icon and URL pathname. However, if you want to set a consistent favicon and page title across all pages, use st.set_page_config after st.navigation, either in the entrypoint file or in the page source. This way, each page will have its own label and icon in the navigation menu, While the browser tab will display a consistent title and favicon for all pages.

python
import streamlit as st

create_page = st.Page("create.py", title="Create entry", icon=":material/add_circle:")
delete_page = st.Page("delete.py", title="Delete entry", icon=":material/delete:")

pg = st.navigation([create_page, delete_page])
st.set_page_config(page_title="Data manager", page_icon=":material/edit:")
pg.run()

st.Page with a Callable

Here's a simple example that demonstrates how to use st.Page with a callable (a function) to define a page in a Streamlit app:

python
import streamlit as st

# Define a simple page as a callable (function)
def about():
    st.title("About")
    st.write("This is the about page of the app.")

# Configure your pages using st.Page
about_page = st.Page(about, title="About Us", icon="ℹ️")

# Register the pages in st.navigation
page = st.navigation([about_page])
page.run()

Section Headers

You can customize the navigation menu to organize the pages using st.navigation. You can also sort, group or remove any pages you don't want the user to access.

For example, you can create two menu states: one for logged-out users, showing only the login page, and another for logged-in users, displaying more options. If an unlogged user tries to access a restricted page, they will be redirected to the login page. After login, users will see a full menu and be directed to the dashboard as the default page.

shell
your-repository/
├── reports
   ├── alerts.py
   ├── bugs.py
   └── dashboard.py
├── tools
   ├── history.py
   └── search.py
└── streamlit_app.py
python
# streamlit_app.py
import streamlit as st

if "logged_in" not in st.session_state:
    st.session_state.logged_in = False

def login():
    if st.button("Log in"):
        st.session_state.logged_in = True
        st.rerun()

def logout():
    if st.button("Log out"):
        st.session_state.logged_in = False
        st.rerun()

login_page = st.Page(login, title="Log in", icon=":material/login:")
logout_page = st.Page(logout, title="Log out", icon=":material/logout:")

dashboard = st.Page(
    "reports/dashboard.py", title="Dashboard", icon=":material/dashboard:", default=True
)
bugs = st.Page("reports/bugs.py", title="Bug reports", icon=":material/bug_report:")
alerts = st.Page(
    "reports/alerts.py", title="System alerts", icon=":material/notification_important:"
)

search = st.Page("tools/search.py", title="Search", icon=":material/search:")
history = st.Page("tools/history.py", title="History", icon=":material/history:")

if st.session_state.logged_in:
    pg = st.navigation(
        {
            "Account": [logout_page],
            "Reports": [dashboard, bugs, alerts],
            "Tools": [search, history],
        }
    )
else:
    pg = st.navigation([login_page])

pg.run()

Statefulness of widgets

As explained in a previous page, you can maintain widget state across pages by using a duplicate key or by interrupting widget clean-up process. In addition to these, when you define the multipage page using st.navigation, you can also have a stateful widget across all pages, by placing it in the entry point file.

NOTE

This method does not work if you define your app with the pages/ directory.

python
import streamlit as st

pg = st.navigation([st.Page("page_1.py"), st.Page("page_2.py")])

st.sidebar.selectbox("Group", ["A","B","C"], key="group")
st.sidebar.slider("Size", 1, 5, key="size")

pg.run()

The selectbox and slider widgets in the sidebar are rendered and stateful on all pages.

Building a Custom Navigation Menu

For more control over your navigation menu, you can hide the default one and build your own. To do this, include position="hidden" in your st.navigation command. If you need a page to be accessible without showing it in the navigation menu, this method is required. Keep in mind, a page must be included in st.navigation to be accessible by any means, including navigation by URL, st.switch_page, or st.page_link. Without this, users won't be able to reach the page.

Page Terminology

In Streamlit, each page is identified by four key elements:

  • Page source: The Python file or callable function containing the page's source code.
  • Page label: The name displayed in the app's navigation menu (See 1).
  • Page title: The content of the HTML <title> element, which appears on the browser tab (See 2).
  • Page URL pathname: The relative URL path of the page from the app's root URL (See 3).

Additionally, a page can have two types of icons:

  • Favicon: The icon displayed next to the page title in the browser tab.
  • Page icon: The icon shown next to the page label in the app's navigation menu.

By default, the page icon and favicon are the same, but you can set them to be different if needed.

Page Terminology

Page Structure and Navigation

This section (and its subsections) applies to both methods of declaring Multipage apps.

IMPORTANT

If you explicitly declare the page title or URL pathname with st.Page, they will take precedence over the following rules.

Page Filenames

Page filenames are composed of four different parts as follows:

  • number: A non-negative integer.
  • separator: Any combination of underscore (_), dash (-), and space ().
  • identifier: Everything up to, but not including, .py.
  • .py extension.

For callables, the function name is the identifier, including any leading or trailing underscores.

TIP

You can also use Emojis in the page names. They will be part of the identifier above.

Page Labels and Titles

Streamlit generates page labels and titles in the following way:

  • If the page has an identifier, Streamlit uses the identifier. It converts any underscores (_) into spaces and removes leading or trailing underscores. Consecutive underscores are collapsed into a single space.
  • If the page has a number but no identifier, Streamlit uses the number unmodified, keeping any leading zeros intact.
  • If the page only has a separator (with no number or identifier), Streamlit will not display the page in the sidebar navigation.

Pages Order

Streamlit pages are sorted using following rules:

  • The entry point file is always displayed first.
  • Then, files that have a number appear before files without a number.
  • Files are sorted based on the number (if any), followed by the label (if any).
  • When files are sorted, Streamlit treats the number as an actual number rather than a string. So 03 is the same as 3.

Page URLs

Streamlit URLs are determined by the following rules (in order):

  • The homepage, typically the entry point file, is linked to the root URL of the app.
  • If the page filename contains an identifier, Streamlit uses it after replacing consecutive spaces () or underscores (_) with a single underscore.
  • If the page is navigated from a callable, Streamlit uses the identifier of the callable unmodified.
  • If the page filename contains a number but no identifier, Streamlit uses the number, preserving any leading zeros.

Users primarily navigate between pages using the navigation widget, which appears in the sidebar by default (for both methods). Optionally, You can hide the default navigation menu and create your own using st.page_link.

See this tutorial to create a custom navigation menu.

If you need to programmatically switch pages, use st.switch_page.

Users can also navigate directly via URLs. If multiple pages share the same URL pathname, Streamlit selects the first one in the navigation menu order.

WARNING

Navigating by URL creates a new browser session, which resets st.session_state. To maintain state across pages, use Streamlit's built-in navigation methods like st.navigation, st.switch_page, st.page_link, or the default menu.

If a user tries to access a non-existent page URL, they will see a "Page not found" modal.