Skip to content

Instantly share code, notes, and snippets.

@sfc-gh-jcarroll
Last active November 27, 2024 09:08
Show Gist options
  • Save sfc-gh-jcarroll/e73f3ac80dadb5d0f2136d9d949c35a9 to your computer and use it in GitHub Desktop.
Save sfc-gh-jcarroll/e73f3ac80dadb5d0f2136d9d949c35a9 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
from typing import Any
import uuid
import streamlit as st
# Requires `pip install streamlit-js`
# https://github.com/toolittlecakes/streamlit_js
from streamlit_js import st_js
KEY_PREFIX = "st_localstorage_"
class StLocalStorage:
"""An Dict-like wrapper around browser local storage.
Values are stored JSON encoded."""
def __init__(self):
# 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"] = {}
# Hide the JS iframes
self._container = st.container()
with self._container:
st.html("""
<style>
.element-container:has(iframe[height="0"]) {
display: none;
}
</style>
""")
def __getitem__(self, key: str) -> Any:
if key not in st.session_state["_ls_unique_keys"]:
st.session_state["_ls_unique_keys"][key] = str(uuid.uuid4())
code = f"""
// The UUID changes on save, which causes this to rerun and update
console.debug('{st.session_state["_ls_unique_keys"][key]}');
return JSON.parse(localStorage.getItem('{KEY_PREFIX + key}'));
"""
with self._container:
result = st_js(code, key=st.session_state["_ls_unique_keys"][key])
if result and result[0]:
return json.loads(result[0])
return None
def __setitem__(self, key: str, value: Any) -> None:
value = json.dumps(value, ensure_ascii=False)
st.session_state["_ls_unique_keys"][key] = str(uuid.uuid4())
code = f"""
console.debug('setting {key} to local storage');
localStorage.setItem('{KEY_PREFIX + key}', JSON.stringify('{value}'));
"""
with self._container:
return st_js(code, key=st.session_state["_ls_unique_keys"][key] + "_set")
def __delitem__(self, key: str) -> None:
st.session_state["_ls_unique_keys"][key] = str(uuid.uuid4())
code = f"localStorage.removeItem('{KEY_PREFIX + key}');"
with self._container:
return st_js(code, key=st.session_state["_ls_unique_keys"][key] + "_del")
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 basic example")
"Any values you save will be available after leaving / refreshing the tab"
key = st.text_input("Key")
value = st.text_input("Value")
if st.button("Save"):
st_local_storage[key] = value
if st.button("Delete"):
del st_local_storage[key]
if key:
f"Current value of {key} is:"
st.write(st_local_storage[key])
@sfc-gh-jcarroll
Copy link
Author

sfc-gh-jcarroll commented Jun 25, 2024

Simple usage

from st_local_storage import st_local_storage as st_ls

key = st.text_input("Key")
value = st.text_input("Value")
if st.button("Save"):
    st_ls[key] = value

if key:
    f"Current value of {key} is:"
    # This will persist across browser sessions
    st.write(st_ls[key])

@sfc-gh-jcarroll
Copy link
Author

WIP adding to streamlit-extras, may get more updates than this gist once merged:
arnaudmiribel/streamlit-extras#233

@duolabmeng6
Copy link

There's a problem.

Bad message format
'setIn' cannot be called on an ElementNode
import streamlit as st
# import st_local_storage
# st_ls = st_local_storage.StLocalStorage()
from st_local_storage import st_local_storage as st_ls

key = st.text_input("Key")
value = st.text_input("Value")
if st.button("Save"):
    st_ls[key] = value

if st.button("Delete"):
    del st_ls[key]

if key:
    f"Current value of {key} is:"
    st.write(st_ls[key])


The writing style that can be used is

import streamlit as st
import st_local_storage
st_ls = st_local_storage.StLocalStorage()

key = st.text_input("Key")
value = st.text_input("Value")
if st.button("Save"):
    st_ls[key] = value

if st.button("Delete"):
    del st_ls[key]

if key:
    f"Current value of {key} is:"
    st.write(st_ls[key])


@duolabmeng6
Copy link

I added get and set methods

# 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
from typing import Any
import uuid
import streamlit as st

# Requires `pip install streamlit-js`
# https://github.com/toolittlecakes/streamlit_js
from streamlit_js import st_js

KEY_PREFIX = "st_localstorage_"


class StLocalStorage:
    """An Dict-like wrapper around browser local storage.

    Values are stored JSON encoded."""

    def __init__(self):
        # 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"] = {}

        # Hide the JS iframes
        self._container = st.container()
        with self._container:
            st.html(""" 
                <style>
                    .element-container:has(iframe[height="0"]) {
                        display: none;
                    }
                </style>
            """)

    def __getitem__(self, key: str) -> Any:
        if key not in st.session_state["_ls_unique_keys"]:
            st.session_state["_ls_unique_keys"][key] = str(uuid.uuid4())
        code = f"""
        // The UUID changes on save, which causes this to rerun and update
        console.debug('{st.session_state["_ls_unique_keys"][key]}');
        return JSON.parse(localStorage.getItem('{KEY_PREFIX + key}'));
        """
        with self._container:
            result = st_js(code, key=st.session_state["_ls_unique_keys"][key])
        if result and result[0]:
            return json.loads(result[0])
        return None

    def __setitem__(self, key: str, value: Any) -> None:
        value = json.dumps(value, ensure_ascii=False)
        st.session_state["_ls_unique_keys"][key] = str(uuid.uuid4())
        code = f"""
        console.debug('setting {key} to local storage');
        localStorage.setItem('{KEY_PREFIX + key}', JSON.stringify('{value}'));
        """
        with self._container:
            return st_js(code, key=st.session_state["_ls_unique_keys"][key] + "_set")

    def __delitem__(self, key: str) -> None:
        st.session_state["_ls_unique_keys"][key] = str(uuid.uuid4())
        code = f"localStorage.removeItem('{KEY_PREFIX + key}');"
        with self._container:
            return st_js(code, key=st.session_state["_ls_unique_keys"][key] + "_del")

    def __contains__(self, key: str) -> bool:
        return self.__getitem__(key) is not None

    def get(self, key: str) -> Any:
        try:
            return self.__getitem__(key)
        except:
            return None

    def set(self, key: str, value: Any) -> None:
        try:
            self.__setitem__(key, value)
        except:
            return None

# st_local_storage = StLocalStorage()
# if __name__ == "__main__":
#     st.title("st_local_storage basic example")
#
#     "Any values you save will be available after leaving / refreshing the tab"
#
#     key = st.text_input("Key")
#     value = st.text_input("Value")
#     if st.button("Save"):
#         st_local_storage[key] = value
#
#     if st.button("Delete"):
#         del st_local_storage[key]
#
#     if key:
#         f"Current value of {key} is:"
#         st.write(st_local_storage[key])

import streamlit as st
import st_local_storage
st_ls = st_local_storage.StLocalStorage()

login = st_ls.get('login')
if login:
    st.markdown(f"### 欢迎回来, **{login}**")
    if st.button("退出登录"):
        st_ls['login'] = None
else:
    st.markdown("## 用户登录")
    username = st.text_input("用户名", max_chars=30, placeholder="输入用户名")
    password = st.text_input("密码", type="password", max_chars=30, placeholder="输入密码")
    if st.button("登录"):
        if username == 'admin' and password == 'admin':
            st_ls.set('login', username)
            st.success("登录成功!正在跳转...")
        else:
            st.error("账号或密码错误")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment