Hide/show pages in multipage app based on conditions

Hi all!

I know that streamlit’s new native support for multipage apps (documented here) works in a way such that each file in the pages folder will be a page, and thus be rendered in the application’s sidebar.

I am trying to make a streamlit application such that when the application is visited, only one page is shown in the sidebar, namely the “Sign In” page. Upon successful sign-in, the application should show the other pages in the sidebar and hide the Sign In page.

To do this, I would need to hide/show pages in the sidebar based on certain conditions (in my case, based on whether the user has successfully signed in). However, it looks like whatever is in the pages folder WILL be rendered as a page on the sidebar by default, and there’s no way to customise which pages get rendered when.

Basically: is there a way to achieve conditional rendering of pages on the sidebar, using Streamlit’s native support for multipage apps?

Thank you!

3 Likes

I just got started with this myself. I’m probably not the best person to answer the question, but I think you might be able to do it with st.session_state?

This is a good topic and I’m curious if Streamlit has any (examples of) integrations with any of the major account creation and authentication providers for self-hosted deployments.

1 Like

Hey @tommaso-moro!

Conditional rendering of pages is a feature request we had quite a lot after releasing multipage apps, indeed! Currently, this is not natively supported, unfortunately, but it’s on our radar for next iterations. What you can do, is having all pages appearing in the menu but have them display different things depending on being signed up or not. Something along those lines:

# pages/sensitive_page.py
if not logged_in():
    st.warning("You must log-in to see the content of this sensitive page! Head over to the log-in page.")
    st.stop()  # App won't run anything after this line

sensitive_stuffs()
6 Likes

And definitely, @hack-r, the logged_in() mocked function above would use st.session_state under the hood!

1 Like

I recently came upon this issue, and I developed two functions that I think should help: one to remove a page from the listed pages on the sidebar, and other to add it back. The code is:

from pathlib import Path
from streamlit.source_util import (
    page_icon_and_name, 
    calc_md5, 
    get_pages,
    _on_pages_changed
)

def delete_page(main_script_path_str, page_name):

    current_pages = get_pages(main_script_path_str)

    for key, value in current_pages.items():
        if value['page_name'] == page_name:
            del current_pages[key]
            break
        else:
            pass
    _on_pages_changed.send()

def add_page(main_script_path_str, page_name):
    
    pages = get_pages(main_script_path_str)
    main_script_path = Path(main_script_path_str)
    pages_dir = main_script_path.parent / "pages"
    script_path = [f for f in pages_dir.glob("*.py") if f.name.find(page_name) != -1][0]
    script_path_str = str(script_path.resolve())
    pi, pn = page_icon_and_name(script_path)
    psh = calc_md5(script_path_str)
    pages[psh] = {
        "page_script_hash": psh,
        "page_name": pn,
        "icon": pi,
        "script_path": script_path_str,
    }
    _on_pages_changed.send()

where main_script_path_str is the name of .py file used to run the app (as in streamlit run file.py), and page_name is the name of the page you wish to add/remove as displayed on the app.

Hope it helps!

5 Likes

@pltoledo Hi I tried your script the delete button is working fine but with add I am getting this error.


My code:

import streamlit as st
import streamlit.components.v1 as components
from pathlib import Path
from streamlit.source_util import (
    page_icon_and_name, 
    calc_md5, 
    get_pages,
    _on_pages_changed
)

def delete_page(main_script_path_str, page_name):

    current_pages = get_pages(main_script_path_str)
    st.write(current_pages)

    for key, value in current_pages.items():
        if value['page_name'] == page_name:
            del current_pages[key]
            break
        else:
            pass
    _on_pages_changed.send()

def add_page(main_script_path_str, page_name):
    
    pages = get_pages(main_script_path_str)
    main_script_path = Path(main_script_path_str)
    pages_dir = main_script_path.parent / "pages"
    script_path = [f for f in pages_dir.glob("*.py") if f.name.find(page_name) != -1][0]
    script_path_str = str(script_path.resolve())
    pi, pn = page_icon_and_name(script_path)
    psh = calc_md5(script_path_str)
    pages[psh] = {
        "page_script_hash": psh,
        "page_name": pn,
        "icon": pi,
        "script_path": script_path_str,
    }
    _on_pages_changed.send()
if st.button("delete"):
    delete_page("Knowledgebase", "Create_Card")
if st.button("add"):
    add_page("Knowledgebase", "Create_Card")

My main page is Knowledgebase and pages are Create Card, Login, test

@Devershi_Vashistha,

I would recommend debuging this by adding this line right above where you search for the script_path:

st.write(list([pages_dir.glob("*.py"))

That way you can see if in fact a file with Create_Card is being found. I wonder if there’s a capitalization mismatch or something similar.

1 Like

@blackary Thank you, for delete it needs _ in between (Create_Card) and for add it doesn’t (Create Card) for some reason.

2 Likes

@blackary @pltoledo But how can I add my main page
image
It doesn’t show the main page name which is Knowledgebase

1 Like

You are just listing the files in pages/

If you want to change that, you could do something like
list(pages_dir.glob("*.py")) + ["Knowledgebase.py"]

1 Like

@blackary Thanks.
I just added main_script_path.parent to pages_dir and it worked.

def add_page(main_script_path_str, page_name):
    
    pages = get_pages(main_script_path_str)
    main_script_path = Path(main_script_path_str)
    pages_dir = main_script_path.parent / "pages"
    # st.write(list(pages_dir.glob("*.py"))+list(main_script_path.parent.glob("*.py")))
    script_path = [f for f in list(pages_dir.glob("*.py"))+list(main_script_path.parent.glob("*.py")) if f.name.find(page_name) != -1][0]
    script_path_str = str(script_path.resolve())
    pi, pn = page_icon_and_name(script_path)
    psh = calc_md5(script_path_str)
    pages[psh] = {
        "page_script_hash": psh,
        "page_name": pn,
        "icon": pi,
        "script_path": script_path_str,
    }
    _on_pages_changed.send()
2 Likes