Pages don't show up on sidebar even when they are in the 'pages' folder

This is the first time I make a post in a forum so any constructive criticism is accepted, thanks!!

I am running a streamlit app locally, not deployed yet, that uses a Google Auth authentication layer. For now the app on Google Cloud is on trial. I am using streamlit version 1.44.0.
Based on whether the user is logged in or not, I want to show all pages in the ‘pages’ folder or just the log in page. However, after logging in, which works fine, the sidebar shows up but the pages in the ‘pages’ folder do not!
I get an error from streamlit saying it does not find page ‘pages/1_page.py’ and saying that only the page/ subdirectory based on the directory where app.py (the .py file where the log in and log out logic is) is supported.

I clearly am using that subdirectory, so I don’t understand why it won’t show up. In the original version of this streamlit app that does not have an authentication layer, the pages in the pages folder would show up on the sidebar automatically, and here they don’t.

The pages are in maindirectory/pages/ with the format 1_page.py, 2_page.py, etc.

The error code is:
streamlit.errors.StreamlitPageNotFoundError: Could not find page: pages\0_Bienvenida.py. You must provide a file path relative to the entrypoint file (from the directory StreamlitLogin-Prueba). Only the entrypoint file and files in the pages/ directory are supported.

Traceback:

File "C:\Users\pablo.sierra\Documents\GitHub\StreamlitLogin-Prueba\app.py", line 40, in <module>
    st.sidebar.page_link(page_path, label=page_name)File "C:\Users\pablo.sierra\AppData\Local\anaconda3\envs\loginstreamlit\Lib\site-packages\streamlit\runtime\metrics_util.py", line 410, in wrapped_func
    result = non_optional_func(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^File "C:\Users\pablo.sierra\AppData\Local\anaconda3\envs\loginstreamlit\Lib\site-packages\streamlit\elements\widgets\button.py", line 698, in page_link
    return self._page_link(
           ^^^^^^^^^^^^^^^^File "C:\Users\pablo.sierra\AppData\Local\anaconda3\envs\loginstreamlit\Lib\site-packages\streamlit\elements\widgets\button.py", line 888, in _page_link
    raise StreamlitPageNotFoundError(

My app.py code is:
import streamlit as st
import os

def login_screen():
st.header(“This app is private.”)
st.subheader(“Please log in.”)
if st.button(“Log in with Google”):
st.login()

if not st.experimental_user.is_logged_in:
login_screen()

# Hide sidebar until user logs in
st.markdown(
    """
    <style>
    [data-testid="stSidebar"] {display: none;}
    </style>
    """,
    unsafe_allow_html=True,
)

else:
st.title(“Welcome!”)
st.write(f"Hello, {st.experimental_user.email}!")

st.sidebar.title("Navigation")

pages_dir = "pages"
if os.path.exists(pages_dir):
    pages = sorted([f for f in os.listdir(pages_dir) if f.endswith(".py")])

    for page in pages:
        page_path = os.path.join(pages_dir, page)
        page_name = page.replace("_", " ").replace(".py", "").title()
        st.sidebar.page_link(page_path, label=page_name)

if st.button("Log out"):
    st.logout()

Can you check that you are using pages/ (plural) everywhere? In your original post, the message you’ve written has singular.

My mistake, it is plural everywhere except for the for loop in which I list each page in the pages folder. I will edit the original post. As you can see in the error message, the root it is trying to find pages at is ‘pages/1_page.py’ with ‘1_page.py’ as a sample file name.

First, can you see if that app sees the pages without trying to manually construction the paths? I’m guessing you are setting client.showSidebarNavigation to False so you can hide the menu before they log in and then manually construct it after they authenticate, right? (Like this tutorial.)

If you set client,showSidebarNavigation to True, are the pages visible in the sidebar?

Correct, I did set client.showSidebarNavigation to False to hide the menu until after authentication. I’m not sure how to turn parts of my text so they show up as code, sorry for that. (edit: I guess I learnt how haha)

Regardless of if its value is True or False, and even if I don’t manually construct the paths and let streamlit do it automatically, these don’t show up on the sidebar. Only the sidebar title shows up.

Edit: it behaves the same way after deleting settings.toml, where I had the client.showSidebarNavigation setting

Do you have:

your-repository/
├── pages/
│   ├── 0_Bienvenida.py
│   ├── 1_page.py
│   └── 2_page.py
└── app.py

You mean config.toml?

Essentially, yes. I actually have 5 pages but all following the same naming scheme, where ‘page’ is the page name. There are neither spaces nor special characters other than underscores _.

Can you share a link to your repository?

Apologies, yes, I meant config.toml.

I will make it public and share a link to the repo shortly, I need do some other thing right now. Thanks a lot for your help up to now, I’ll be back soon.

1 Like

Hi again @mathcatsand,
Sorry for the delay, I didn’t have time yesterday to keep going.

While making a cut-down public repo with sample files only, I managed to have all pages show up on the left sidebar and have the authentication layer work.

The things I did were:

  • Hide the sidebar after st.set_page_config() by using:
st.markdown("""
        <style>
        [data-testid="stSidebarNav"] {display: none;}  /* Hide default multi-page menu */
        </style>
    """, unsafe_allow_html=True)
  • Also hide the sidebar in the .streamlit/config.toml by including [ui] hideSidebarNav = true . This might be redundant in combination with the previous step, but I got me the result I wanted.
  • Ensure the filename for each page had its .py extension when creating a st.sidebar.radio() selector. I’m pretty sure the name was always correct and that the main culprit is:
  • Ensure each page is typed as-is within its own .py file and not within a Python function that is later executed later on after a if __name__ == "__main__": statement. This was probably not the way to code a streamlit site but it worked when I started using streamlit and never changed it.

I ended up not finalising the public repo because once I fixed the error I kept adding all the original sites back and due to confidentiality clauses I cannot post them on a public GitHub. But if you’d like me to show some more of the code let me know.

Thanks for your help!

The code I wrote for the sidebar page selector is below:

def sidebar_navigation():
    st.sidebar.markdown(
    "<h3 style='text-align: left; font-size: 22px; margin-bottom: -60px;'>Selecciona una página:</h3>", 
    unsafe_allow_html=True
    )

    PAGES = {
        "🏠 XXX": "app.py",
        "📊 AAAAAAAAAAAA": "pages/1_AAA.py",
        "📊 BBBBBBBBBBB": "pages/2_BBB.py",
        "🥇 CCCCCCCCCCCC": "pages/3_CCC.py",
        "🧾 DDDDDDDDDDD": "pages/4_DDD.py"
    }

    # Get current page from session state or set default
    if "current_page" not in st.session_state:
        st.session_state.current_page = "🏠 XXX"

    # Create the radio selector and preselect the current page
    selected_page = st.sidebar.radio("", list(PAGES.keys()), index=list(PAGES.keys()).index(st.session_state.current_page))

    # If the user selects a different page, update session state and switch
    if selected_page != st.session_state.current_page:
        st.session_state.current_page = selected_page
        st.switch_page(PAGES[selected_page])
    
    # Logout button
    if st.sidebar.button("Log out"):
        st.logout()

If you managed to strip it down to a working example, does that mean you were able to figure out which piece breaks it when you add it back in?