Saving data in local storage via streamlit

Hi, I was wondering if there was a way to store some fields in the browser local storage of a client and access them through streamlit. I want to be able to repopulate certain fields if the user closes my form and opens it after some time.

If this isn’t feasible is using cookies my best option and is there a good way of setting and updating cookie keys via streamlit?

Hi @pramodith, welcome to the forum. There’s st.experimental_set_query_params and st.experimental_get_query_params that allows you to send field information via query parameters in the browser’s URL bar.

More info here:

2 Likes

Hey @dataprofessor thanks for the pointer but I’m afraid I’m looking for something else. In my application the user should be able to close the url to my application, but on returning certain fields of the user need to be restored. The application does not have a login feature so we want to store these fields in the clients browser via local storage or through cookies.

2 Likes

Hi @pramodith

Would this Streamlit component be useful? It’s intended to help you manage your client’s cookies streamlit-cookies-manager · PyPI

Let us know! :relaxed:

3 Likes

Sir, I had a similar doubt. Can these be used for this? Or is there another way? I have not deployed my app yet, if I deploy it, will I be able to keep track of some ss variables from all the open instances at a central location?

1 Like

Hi @arnaud I’ve tried using this module but I keep running into an error. I’ve written this simple function to update any of the keys of the cookie.

def update_cookie_state(key, value):
    cookie_manager_obj = get_manager()
    tomorrow = datetime.datetime.now() + datetime.timedelta()
    if cookie_manager_obj.get(key):
        cookie_manager_obj.delete(key)
    cookie_manager_obj.set(key, value, expires_at=datetime.datetime(year=tomorrow.year, month=tomorrow.month, day=tomorrow.day))

However, when I try running this I get the following error :

streamlit.errors.DuplicateWidgetID: There are multiple identical st.extra_streamlit_components.CookieManager.cookie_manager widgets with
key='set'.
To fix this, please make sure that the key argument is unique for
each st.extra_streamlit_components.CookieManager.cookie_manager you create.

I’m not sure which key is not unique in this case.

1 Like

@Ishan_Mistry I answered in your thread, I think a fairly common solution to your problem is a database!

1 Like

Hey @pramodith

I’ve just built a synchronous way to access localStorage from Streamlit using websockets (streamlit-ws-localstorage), feel free to try it out.

I struggled for a couple of days with localStorage access (and authentication), and thought it would be easier to build a websocket based synchronous communication itself. The code is simple, just import the module and use it like this:

import streamlit as st
from streamlit_ws_localstorage import injectWebsocketCode, getOrCreateUID

# Main call to the api, returns a communication object
conn = injectWebsocketCode(hostPort='linode.liquidco.in', uid=getOrCreateUID())

st.write('setting into localStorage')
ret = conn.setLocalStorageVal(key='k1', val='v1')
st.write('ret: ' + ret)

st.write('getting from localStorage')
ret = conn.getLocalStorageVal(key='k1')
st.write('ret: ' + ret)

Here is a demo of fetching saved info in the browser:

Installation: pip install streamlit-ws-localstorage
Repository: GitHub - gagangoku/streamlit-ws-localstorage: A simple synchronous way of accessing localStorage from your Streamlit app.
On pypi: streamlit-ws-localstorage · PyPI

4 Likes

the “key” means the wiget_id ,not key in dict. when you use the wiget like get,set,delete, you should not use same Key, you can apply like this:key=“0”, “1”,“2”,etc,it works

2 Likes

I tried your code verbatim. But it throws an error:

ssl.SSLCertVerificationError: This app has encountered an error. The original error message is redacted to prevent data leaks. Full error details have been recorded in the logs (if you're on Streamlit Cloud, click on 'Manage app' in the lower right of your app).
Traceback:
File "/home/adminuser/venv/lib/python3.9/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 535, in _run_script
    exec(code, module.__dict__)
File "/mount/src/xxxxx/xxxxx.py", line 8, in <module>
    ret = conn.setLocalStorageVal(key='k1', val='v1')
File "/home/adminuser/venv/lib/python3.9/site-packages/streamlit_ws_localstorage/__init__.py", line 99, in setLocalStorageVal
    result = self.sendCommand(json.dumps({ 'cmd': 'localStorage_set_key', 'key': key, 'val': val }))
File "/home/adminuser/venv/lib/python3.9/site-packages/streamlit_ws_localstorage/__init__.py", line 90, in sendCommand
    self.loop.run_until_complete(query(future1))
File "/usr/local/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
    return future.result()
File "/home/adminuser/venv/lib/python3.9/site-packages/streamlit_ws_localstorage/__init__.py", line 80, in query
    async with websockets.connect("wss://" + self.hostPort + "/?uid=" + self.uid, ssl=ssl_context) as ws:
File "/home/adminuser/venv/lib/python3.9/site-packages/websockets/legacy/client.py", line 629, in __aenter__
    return await self
File "/home/adminuser/venv/lib/python3.9/site-packages/websockets/legacy/client.py", line 647, in __await_impl_timeout__
    return await self.__await_impl__()
File "/home/adminuser/venv/lib/python3.9/site-packages/websockets/legacy/client.py", line 651, in __await_impl__
    _transport, _protocol = await self._create_connection()
File "/usr/local/lib/python3.9/asyncio/base_events.py", line 1090, in create_connection
    transport, protocol = await self._create_connection_transport(
File "/usr/local/lib/python3.9/asyncio/base_events.py", line 1120, in _create_connection_transport
    await waiter
File "/usr/local/lib/python3.9/asyncio/sslproto.py", line 534, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
File "/usr/local/lib/python3.9/asyncio/sslproto.py", line 188, in feed_ssldata
    self._sslobj.do_handshake()
File "/usr/local/lib/python3.9/ssl.py", line 945, in do_handshake
    self._sslobj.do_handshake()

How did you overcome the SSL certificates error?

1 Like

Hey @DevBhuyan

Can you try it with wsauthserver.supergroup.ai, recently switched servers.

1 Like

If I understand correctly, this solution requires a second server to be running somewhere. Is that correct?

1 Like

Yes @shawngiese

streamlit-ws-localstorage/streamlit_ws_localstorage/auth_redirect_server at main · gagangoku/streamlit-ws-localstorage · GitHub has the code for the auth redirect server.

You can host your own, or use wsauthserver.supergroup.ai.

2 Likes

I used streamlit-javascript ibrary to handle it:

from streamlit_javascript import st_javascript

def local_storage_get(key):
    return st_javascript(f"localStorage.getItem('{key}');")

def local_storage_set(key, value):
    value = json.dumps(value, ensure_ascii=False)
    return st_javascript(f"localStorage.setItem('{key}', JSON.stringify('{value}');")

The only problem was, that due to asynchronous nature of streamlit components, st_javascript did not provide return value of js code immidiatelly. Instead, it reloads some number of times before providing the value. And in case of None, you just don’t know if it returns empty value because you js code returns null or because the code execution hasn’t finished yet. So, the following code didn’t work

if "token" not in st.session_state:
    st.session_state.token = local_storage_get("token")

It just saved 0 - initial value of st_javascript component.


So, i build my own version of this lib, that allows distinguishing between “not ready yet” and “returns none” states.

Feel free to check it out here

Brief explanation:
It returns [] if the code execution is in progress and [] if it finishes. So, you can just st.stop() until result is not empty or for unblocking case, just check it before setting session_state value

if "token" not in st.session_state:
    if result := local_storage_get("token"): 
        st.session_state.token = result[0]
2 Likes

Hey @toolittlecakes I wanted to try this out but both links in your post seem to go to the original repo? is there a link to your fork somewhere? Thanks!

EDIT: Nvm, i found it from guessing your github username :smile: for anyone else curious: