I want to hide a page so it’s only accessible directly by URL, I’ve tried st-pages but I’ve been unable to make it work. Also is there a way to edit the CSS before it first loads, I know you can inject CSS using st.markdown but that loads after original CSS meaning the user will see the original CSS for a split second.
The only way (as far as I know) to hide pages but still keep them accessible by url is using CSS.
Here’s one way that hides pages using CSS by looking for the href
that they point to:
import streamlit as st
def _main_page():
st.write("Main Page")
def _page_2():
st.write("Page 2")
def _page_3():
st.write("Page 3")
def _page_4():
st.write("Page 4")
def _page_5():
st.write("Page 5")
main_page = st.Page(_main_page, title="Main Page", icon="🎈")
page_2 = st.Page(_page_2, title="Page 2", icon="❄️", url_path="page_2")
page_3 = st.Page(_page_3, title="Page 3", icon="🎉", url_path="page_3")
other_pages = [
st.Page(_page_4, title="Page 4", icon="❄️", url_path="page_4"),
st.Page(_page_5, title="Page 5", icon="🎉", url_path="page_5"),
]
pg = st.navigation({"Main": [main_page, page_2, page_3], "Other": other_pages})
pages_to_hide = st.multiselect(
"Pages to hide", ["page_2", "page_3", "page_4", "page_5"], default=["page_2"]
)
for page in pages_to_hide:
# Find links in the sidebar that have a href that ends with page_2, page_3, etc. and hide them
st.html(f"""
<style>
[data-testid="stSidebarNavLink"][href$="{page}"] {{
display: none;
}}
</style>
""")
pg.run()
Also, if you have css like this that is in streamlit_app.py instead of in a page itself, it runs once and not every time you switch pages. Also, using st.html is also recommended over using st.markdown.
Two other solutions:
- You can hide the default navigation with
position="hidden"
and build a manual navigation widget withst.page_link
. - You can use
st.context.url
to check the path in the URL and insert it into your pages list/dict right before callingst.navigation
so that it only appears when the user is trying to go there.
import streamlit as st
def _main_page():
st.write("Main Page")
def _page_2():
st.write("Page 2")
def _page_3():
st.write("Page 3")
def _page_4():
st.write("Page 4")
def _page_5():
st.write("Page 5")
main_page = st.Page(_main_page, title="Main Page", icon="🎈")
page_2 = st.Page(_page_2, title="Page 2", icon="❄️", url_path="page_2")
page_3 = st.Page(_page_3, title="Page 3", icon="🎉", url_path="page_3")
page_4 = st.Page(_page_4, title="Page 4", icon="❄️", url_path="page_4")
page_5 = st.Page(_page_5, title="Page 5", icon="🎉", url_path="page_5")
pages = [main_page, page_2]
hidden_pages = {
"page_3": page_3,
"page_4": page_4,
"page_5": page_5
}
path = st.context.url.split("/")[-1]
if path in hidden_pages:
pages.append(hidden_pages[path])
pg = st.navigation(pages)
pg.run()
Also, here’s a feature request you can upvote: Add a page without showing it in the navigation menu · Issue #10738 · streamlit/streamlit · GitHub
Thanks, I ended up going with an approach like this. I hid all pages with showSidebarNavigation = false
then in each page I added
st.sidebar.title("Navigation") st.sidebar.page_link('View.py', label='**View** Posts') st.sidebar.page_link('pages/2_Upload.py', label='**Upload** New Post')
with this approach, my secret page doesn’t appear in the sidebar but it’s still accessible by URL.
It isn’t the best since it still takes a split second for the sidebar to appear but it works and is simple.