How to use dropdown with url query parameter

I am trying to use a streamlit dropdown injunction with a query parameter in the URL such that they match at all times. So if a user selects an option in the dropdown, the URL query is updated to match and vice versa.

I tried implementing as the folllowing but it was not updating immediately and always required the user to select an option twice to update:

query_params = st.experimental_get_query_params()
query_option = query_params['option'][0]

options = ['cat', 'dog', 'mouse', 'bat', 'duck']
option_selected = st.sidebar.selectbox('Pick option',
                                        options,
                                        index=options.index(query_option))

st.experimental_set_query_params(option=option_selected)

If you’re wondering why i am trying to do this in the first place, my reasoning is I’d like to share a link with a specific selection to an end-user without them having to select it among the hundreds of options in the dropdown. So if there is another way to share a link with a dropdown option selected, im open to that as well.

Hi @tbrown :wave:

To get your example working, I’ve checked if query parameters exist. If they don’t exist, e.g. on launch, I set them to predefined value. After launch, once you set the query parameter from the dropdown, and share the url containing the query parameter, users should see their dropdown option match with the query parameter in the url.

Here’s what happens in the code below:

  1. When the app is launched, there is no query parameter. The app url is http://host:port.

    • This case is covered by the except block which sets a predefined query parameter. Defaults to dog. Users can set the query parameter from the dropdown.
  2. Once the query parameter is set from the dropdown in step 1 :point_up:, we retrieve the query parameter with st.experimental_get_query_params() in the try block.

  3. Now, when you share your app url with the query parameter included, e.g. http://localhost:8501/?option=mouse, users visiting that url will have their dropdown option matching the query parameter, e.g. mouse.

import streamlit as st

# query params exist
try:
    options = ['cat', 'dog', 'mouse', 'bat', 'duck']

    query_params = st.experimental_get_query_params()
    query_option = query_params['option'][0] #throws an exception when visiting http://host:port

    option_selected = st.sidebar.selectbox('Pick option',
                                            options,
                                            index=options.index(query_option))
    if option_selected:
        st.experimental_set_query_params(option=option_selected)

# run when query params don't exist. e.g on first launch
except: # catch exception and set query param to predefined value
    options = ['cat', 'dog', 'mouse', 'bat', 'duck']
    st.experimental_set_query_params(option=options[1]) # defaults to dog

    query_params = st.experimental_get_query_params()
    query_option = query_params['option'][0]

    option_selected = st.sidebar.selectbox('Pick option',
                                            options,
                                            index=options.index(query_option))
    if option_selected:
        st.experimental_set_query_params(option=option_selected)

Output:

query-params

Does this help? :grinning_face_with_smiling_eyes:

Happy Streamlit-ing! :balloon:
Snehan

2 Likes

Hello, I tried to run this example but I face an unexpected behavior after the first option change: I have to select twice an option to have the URL actually updated. After the first selection, I see that it is taken into account for a small period of time and then the previous selection is restored.

I tested this with streamlit 0.82.0 and 1.8.1 to see if it is a regression but they both show the same behavior.

1 Like

Hi @v1ncentb, welcome to the community!! :partying_face: :wave:

And good catch! My previous solution was indeed buggy. I’ve verified that the code below doesn’t require you to select an option twice to have the URL actually updated in Streamlit 1.8.1:

import streamlit as st

# callback to update query param on selectbox change
def update_params():
    st.experimental_set_query_params(option=st.session_state.qp)

options = st.selectbox(
    "Param", ["cat", "dog", "mouse", "bat", "duck"], key="qp", on_change=update_params
)

query_params = st.experimental_get_query_params()

# set the initial query param on first run
# based on the default option in selectbox
if not query_params:
    st.experimental_set_query_params(option=st.session_state.qp)

# display for debugging purposes
query_params = st.experimental_get_query_params()
st.write(query_params)

Happy Streamlit-ing! :balloon:
Snehan

2 Likes

@snehankekre - Thanks for this snippet. I tried it too and noticed it doesn’t change the select box value based on a supplied query param in the URL. This change fixes it (I’m using st.radio just 'cos it’s quicker to change values):

import streamlit as st

# callback to update query param on selectbox change
def update_params():
    st.experimental_set_query_params(option=st.session_state.qp)

options = ["cat", "dog", "mouse", "bat", "duck"]

query_params = st.experimental_get_query_params()

# set selectbox value based on query param, or provide a default
ix = 0
if query_params:
    try:
        ix = options.index(query_params['option'][0])
    except ValueError:
        pass

selected_option = st.radio(
    "Param", options, index=ix, key="qp", on_change=update_params
)

# set query param based on selection
st.experimental_set_query_params(option=selected_option)

# display for debugging purposes
st.write('---', st.experimental_get_query_params())
1 Like

Thank you for your quick support. It solves the issue :+1:

@v1ncentb @asehmi I made a library of url-aware versions of input controls that automatically sync their state with the url query string: streamlit-permalink.