Docs.streamlit.io-create a Dynamic Navigation Menu
Docs.streamlit.io-create a Dynamic Navigation Menu
docs.streamlit.io/develop/tutorials/multipage/dynamic-navigation
Documentation
search
Search
st.navigation makes it easy to build dynamic navigation menus. You can change
the set of pages passed to st.navigation with each rerun, which changes the
navigation menu to match. This is a convenient feature for creating custom, role-
based navigation menus.
This tutorial uses st.navigation and st.Page, which were introduced in Streamlit
version 1.36.0. For an older workaround using the pages/ directory and
st.page_link, see Build a custom navigation menu with st.page_link.
Applied concepts
1/8
Prerequisites
streamlit>=1.36.0
Summary
In this example, we'll build a dynamic navigation menu for a multipage app that
depends on the current user's role. You'll abstract away the use of username and
credentials to simplify the example. Instead, you'll use a selectbox to let users
choose a role and log in.
The entrypoint file, streamlit_app.py will handle user authentication. The other
pages will be stubs representing account management (settings.py) and specific
pages associated to three roles: Requester, Responder, and Admin. Requesters can
access the account and request pages. Responders can access the account and
respond pages. Admins can access all pages.
Complete codeexpand_more
Directory structure:
streamlit_app.py:
2/8
title="Settings", icon=":material/settings:") request_1 = st.Page(
"request/request_1.py", title="Request 1", icon=":material/help:",
default=(role == "Requester"), ) request_2 = st.Page(
"request/request_2.py", title="Request 2", icon=":material/bug_report:"
) respond_1 = st.Page( "respond/respond_1.py", title="Respond 1",
icon=":material/healing:", default=(role == "Responder"), ) respond_2 =
st.Page( "respond/respond_2.py", title="Respond 2",
icon=":material/handyman:" ) admin_1 = st.Page( "admin/admin_1.py",
title="Admin 1", icon=":material/person_add:", default=(role ==
"Admin"), ) admin_2 = st.Page("admin/admin_2.py", title="Admin 2",
icon=":material/security:") account_pages = [logout_page, settings]
request_pages = [request_1, request_2] respond_pages = [respond_1,
respond_2] admin_pages = [admin_1, admin_2] st.title("Request manager")
st.logo("images/horizontal_blue.png",
icon_image="images/icon_blue.png") page_dict = {} if
st.session_state.role in ["Requester", "Admin"]: page_dict["Request"] =
request_pages if st.session_state.role in ["Responder", "Admin"]:
page_dict["Respond"] = respond_pages if st.session_state.role ==
"Admin": page_dict["Admin"] = admin_pages if len(page_dict) > 0: pg =
st.navigation({"Account": account_pages} | page_dict) else: pg =
st.navigation([st.Page(login)]) pg.run()
Your app will be blank since you still need to add code.
import streamlit as st
3/8
4. Save your streamlit_app.py file and view your running app.
5. Click "Always rerun" or hit your "A" key in your running app.
In later steps, you'll create an authentication method that saves the current
user's role to st.session_state.role. Since you'll be blocking access to this
page until a user is logged in, you don't need to initialize the "role" key in
Session State for this page.
3. Create similar stubs by changing the value of st.header for the following six
pages:
horizontal_blue.png
icon_blue.png
You now have all the files needed to build your app.
4/8
1. Return to streamlit_app.py and initialize "role" in Session State.
You will use this value to gatekeep access to your app. This represents the role
of the current, authenticated user.
st.navigation lets you define pages from Python functions. Here, you'll define the
login and logout pages from Python functions.
def login():
st.header("Log in")
def logout():
5/8
6. Immediately set the role to None and rerun the app.
Since the lougout page function immediately updates Session State and
reruns, a user will never view this page. The page will execute in a fraction of a
second and, upon rerunning, the app will send the user to the login page.
Therefore, no additional elements are rendered on the page. If desired, you
can change this page to also include a button, similar to the login page. A
button would allow users to confirm they really intend to log out.
role = st.session_state.role
This gives each page a nice title and icon to make your navigation menu look
neat and clean.
If you don't manually declare a default page in st.navigation, then the first
page will automatically be the default. The first page in the menu will be "Log
out" within an "Account" section of the menu. Therefore, you'll need to tell
Streamlit what page each user should be directed to by default.
This code dynamically sets default=True when the role is "Requester" and
sets it to False, otherwise.
6/8
4. Define your remaining pages.
Similar to the request pages, the default parameter is set for the other roles'
default pages.
st.title("Request manager")
Since you're calling the title command in your entrypoint file, this title will be
visible on all pages. Elements created in your entrypoint file create a frame of
common elements around all your pages.
st.logo("images/horizontal_blue.png",
icon_image="images/icon_blue.png")
Once again, since you're calling this command in your entrypoint file, you won't
need to also call it within each page.
7/8
3. Initialize a dictionary of page lists.
page_dict = {}
In the next step, you'll check the user's role and add pages to the dictionary
that the user is allowed to access. When st.navigation receives a dictionary
of page lists, it creates a navigation menu with groups of pages and section
headers.
5. Check if the user is allowed to access any pages and add the account pages if
they are.
If page_dict is not empty, then the user is logged in. The | operator merges
the two dictionaries, adding the account pages to the beginning.
else: pg = st.navigation([st.Page(login)])
pg.run()
Try logging in, switching pages, and logging out. Try again with a different role.
forum
8/8