Skip to content

Instantly share code, notes, and snippets.

@jfbercher
Forked from snehankekre/st_local_storage.py
Created March 6, 2026 18:55
Show Gist options
  • Select an option

  • Save jfbercher/f5918b5a97786f26ef9971e395b88742 to your computer and use it in GitHub Desktop.

Select an option

Save jfbercher/f5918b5a97786f26ef9971e395b88742 to your computer and use it in GitHub Desktop.
Local storage access in Streamlit - st_local_storage
# NOTE: This feature uses browser local storage! AKA it stores data on a viewer's
# machine. This may have privacy and compliance implications for your app. Be sure
# to take that into account with any usage.
import json
import time
import uuid
from typing import Any, Optional
from streamlit.time_util import time_to_seconds
# Requires `pip install streamlit-js`
# https://github.com/toolittlecakes/streamlit_js
from streamlit_js import st_js
import streamlit as st
KEY_PREFIX = "st_localstorage_"
# Keep track of a UUID for each key to enable reruns
if "_ls_unique_keys" not in st.session_state:
st.session_state["_ls_unique_keys"] = {}
_ls_keys = st.session_state["_ls_unique_keys"]
# Hide the JS iframes
st.html("""
<style>
.element-container:has(iframe[height="0"]) {
display: none;
}
</style>
""")
class StLocalStorage:
"""
A dict-like wrapper around browser local storage.
Values are stored JSON encoded.
It allows you to store data in the browser's local storage with an optional TTL (Time to Live).
TTL is the maximum time to keep an entry in the cache. Can be one of:
- `None` if cache entries should never expire (default).
- A number specifying the time in seconds.
- A string specifying the time in a format supported by Pandas's Timedelta constructor, e.g. `"1d"`, `"1.5 days"`, or `"1h23s"`.
- A `timedelta` object from Python's built-in datetime library, e.g. `timedelta(days=1)`.
Examples
--------
Without TTL
>>> st_local_storage = StLocalStorage()
>>> st_local_storage['key'] = 'value'
>>> st.write(st_local_storage['key'])
With TTL in seconds
>>> st_local_storage = StLocalStorage()
>>> st_local_storage['key'] = 'value', 10.0
>>> st.write(st_local_storage['key']) # Will return None after 10 seconds
With TTL in string format
>>> st_local_storage = StLocalStorage()
>>> st_local_storage['key'] = 'value', '10s'
>>> st.write(st_local_storage['key']) # Will return None after 10 seconds
"""
def __getitem__(self, key: str) -> Optional[Any]:
if key not in _ls_keys:
_ls_keys[key] = str(uuid.uuid4())
code = f"""
// The UUID changes on save, which causes this to rerun and update
console.debug('{_ls_keys[key]}');
return localStorage.getItem('{KEY_PREFIX + key}');
"""
result = st_js(code)
if result and result[0]:
data = json.loads(result[0])
if 'expiry' in data and data['expiry'] is not None and data['expiry'] < time.time():
self.__delitem__(key) # Remove expired item
return None
return data['value']
return None
def __setitem__(self, key: str, value: Any) -> None:
ttl = None # Default TTL to None
if isinstance(value, tuple):
value, ttl = value
ttl = time_to_seconds(ttl)
expiry = time.time() + ttl if ttl is not None else None
data = {'value': value, 'expiry': expiry}
data_json = json.dumps(data)
_ls_keys[key] = str(uuid.uuid4())
code = f"localStorage.setItem('{KEY_PREFIX + key}', {json.dumps(data_json)});"
print(f"Setting {key} to {value} with expiry {expiry}. Current time: {time.time()}")
st_js(code)
def __delitem__(self, key: str) -> None:
_ls_keys[key] = str(uuid.uuid4())
code = f"localStorage.removeItem('{KEY_PREFIX + key}');"
st_js(code)
def __contains__(self, key: str) -> bool:
return self.__getitem__(key) is not None
st_local_storage = StLocalStorage()
if __name__ == "__main__":
st.title("st_local_storage with TTL")
"Any values you save will be available after leaving / refreshing the tab, unless they expire."
key = st.text_input("Key")
value = st.text_input("Value")
ttl = None
if st.toggle("Set TTL?"):
if st.checkbox("Use seconds"):
ttl = st.number_input("TTL in seconds", 10.0)
else:
ttl = st.text_input("TTL as string, e.g. 1d, 1h30m", "10s")
if st.button("Save"):
st_local_storage[key] = value, ttl
if key:
f"Current value for {key} is:"
st.write(st_local_storage[key])
if st.button("Delete"):
del st_local_storage[key]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment