A page_link to the same page doesn't properly update query parameters.

Hi! I’m new to Streamlit (and it’s my first post here).

I’m not sure whether this is expected behaviour or a bug, but when st.page_link leads to the current page, the query parameters in the address bar aren’t updated. The new parameter values can be read from st.query_params, but they revert to their previous values on rerun.

I’m running Streamlit v1.57.0 on Python 3.14.4 locally. Here is the minimal code example.

import streamlit as st

def page_a():
    pass

def page_b():
    pass

pages = st.Page(page_a), st.Page(page_b)
st.navigation(pages)
for page in pages:
    with st.container(horizontal=True):
        st.write(page.title)
        for x in range(1, 6):
            st.page_link(page, label=f"{x=}", query_params={"x": str(x)})
st.write("x:", st.query_params.get("x", ""))

When I click on links to another page, everything works as expected: the query string updates and the selected value is displayed. However, if I click on links to the same page, the query string doesn’t update, and on the next rerun, the displayed value reverts to the previous state.

My current workaround is to update the value manually like this:

if "x" in st.query_params:
    st.query_params.x = st.query_params.x

But this looks strange, so I decided to bring up the matter here.

Welcome to the Streamlit community, and thanks for sharing a clear minimal example! :balloon: What you’re seeing is expected: when you use st.page_link to link to the current page, Streamlit does not update the browser’s query parameters or rerun the script, so the address bar and st.query_params don’t reflect the new values on rerun. This is different from navigating to a different page, which triggers a full rerun and updates the URL as expected. Your manual workaround is a common approach, but it’s a bit clunky.

This behavior is not explicitly documented as a bug, but it’s a known limitation of how st.page_link handles navigation to the current page. If you want the query parameters to update when clicking a link to the same page, you’ll need to manually set them as you did, or use a different navigation pattern (like a button with an on_click callback that updates st.query_params). For more details, see the st.page_link documentation and related discussions.

Sources:

Thank you for your detailed response! It’s helpful to know that this is a known limitation rather than a hidden flaw. I could leave it at that (and probably should), but I’d like to offer a couple of thoughts (not as an objection, but for a potential discussion).

The current implementation appears inconsistent in at least three respects.

  1. The behavior of st.page_link in the specific case (the same page) differs from the general behavior. I understand that the case of the same page is treated differently for good reason, but this can be inconvenient if it is not known in advance on which page this function is being called.

  2. Calling st.page_link for the same page “seems to work”: it doesn’t trigger any warnings and updates the corresponding values in st.query_params, which, however, are lost on next rerun.

  3. The sister function st.switch_page does not have this limitation. If you replace st.page_link(page, label=f"{x=}", query_params={"x": str(x)}) with if st.button(label=f"{page.title} {x=}"): st.switch_page(page, query_params={"x": str(x)}) in the example above, it behaves similarly in other respects but correctly updates the values in the address bar (and keeps them on rerun).

Overall, from my perspective as a user, this seems like something that could benefit from improving. But given your response, it’s probably not worth filing a bug report (especially since I’m not very familiar with GitHub). I don’t want to waste anyone’s time; just wanted to share my opinion in case it might be useful :slight_smile:

P.S. OMG, only now I realized I was basically talking to a bot, LOL) I like having discussions with LLMs, so that’s okay. It’s just I usually know it in advance :smiley: