# Multi-page app with session state

**URL:** <https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074>\
**Category:** Show the Community!\
**Tags:** session-state\
**Created:** [May 26, 2020, 11:24pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074 "2020-05-26T23:24:58Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [May 26, 2020, 11:24pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/1 "2020-05-26T23:24:58Z")

</div>

### Edit: Streamlit 0.84.0 has officially introduced session state. Go check it out: [Session State for Streamlit 🎈](https://blog.streamlit.io/session-state-for-streamlit/)

* * *

Hello there 👋

For a project I needed to build multi-page app with a settings page. As I’ve seen this [topic](https://discuss.streamlit.io/t/preserving-state-across-sidebar-pages/107) requesting the feature, I decided to share the solution I came up with. It features a custom version of @thiago’s [SessionState](https://gist.github.com/tvst/036da038ab3e999a64497f42de966a92).

It still relies on hacks, but it should work in most cases. It was tested with Streamlit version 0.61.0 (and now 0.65).

Things you must be aware of before using it:

- **You must call `state.sync()` at the end of your app if you set a widget’s default value to a state variable.** This function triggers a rerun if you store a new state value or [update an already stored object](https://gist.github.com/okld/0aba4869ba6fdc8d49132e6974e2e662#gistcomment-3339119). This is how I fix rollback issues when you interact with widgets bound to a state value. In my demo I call it at the end of `main()`.

- If you use `state.sync()`, you won’t be able to store objects that Streamlit cannot hash, just like when you use `@st.cache` and you get a [`UnhashableTypeError`](https://docs.streamlit.io/en/stable/troubleshooting/caching_issues.html#how-to-fix-an-unhashabletypeerror).

- If I start to drag a slider, 1 second later the mouse is released, probably because of my rerun 🤷‍♂️

 ![streamlit-app-2020-05-27-12-05-97.webm](https://global.discourse-cdn.com/streamlit/original/2X/f/f61df1388acc385d55a542789768612e7f7ff8ec.gif)

> <https://gist.github.com/okld/0aba4869ba6fdc8d49132e6974e2e662>

---

<div class="post-metadata">

**Author:** ![vnguyendc](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/vnguyendc/32/1657_2.png) [@vnguyendc](https://discuss.streamlit.io/u/vnguyendc)\
**Post date:** [May 28, 2020, 2:59pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/2 "2020-05-28T14:59:30Z")

</div>

Thanks for this demo! So when calling \_get\_state, I can then just declare any input/data to store in the session state within any page function?

---

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [May 28, 2020, 3:30pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/3 "2020-05-28T15:30:14Z")

</div>

Hello @vnguyendc,

Exactly. To sum up how you can interact with the state object:

- You can assign data like so : `state.my_value = "my value"`
- Accessing an undefined value will return `None` instead of throwing an exception
- You can initialize a value only once by using the state object as a function: `state(my\_value=“init value”).

I’ll add a little bit more documentation to the gist later on.

---

<div class="post-metadata">

**Author:** ![vnguyendc](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/vnguyendc/32/1657_2.png) [@vnguyendc](https://discuss.streamlit.io/u/vnguyendc)\
**Post date:** [May 28, 2020, 6:50pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/4 "2020-05-28T18:50:25Z")

</div>

One thing I want to do is preserve a pandas dataframe across pages, however I get this error when trying to store a dataframe in a session state:

```auto
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

```

Mainly due to the comparison being done in this line:

```auto
 if (item not in data or data[item] != value) and item not in self._item_rerun:

```

Any idea how to work around this?

---

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [May 28, 2020, 8:05pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/5 "2020-05-28T20:05:20Z")

</div>

Thanks for your feedback.

I’ve updated the session state implementation in the gist. Instead of doing a naive value comparison, I normalize them first using an internal streamlit function used in their caching system. Now, every object supported by `st.cache` should work fine.

I’ve also added the possibility to access state values like so : `state["my_input"]`. This can be useful when you need to assign values dynamically.

Tell me if it works better for you now.

---

<div class="post-metadata">

**Author:** ![vnguyendc](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/vnguyendc/32/1657_2.png) [@vnguyendc](https://discuss.streamlit.io/u/vnguyendc)\
**Post date:** [May 28, 2020, 11:34pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/6 "2020-05-28T23:34:04Z")

</div>

Thanks a lot for this. This is very helpful. I’ll definitely provide more feedback as I continue to implement this code in the application I’m building.

---

<div class="post-metadata">

**Author:** ![vnguyendc](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/vnguyendc/32/1657_2.png) [@vnguyendc](https://discuss.streamlit.io/u/vnguyendc)\
**Post date:** [June 2, 2020, 12:17am UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/7 "2020-06-02T00:17:44Z")

</div>

Hi Synode,

The session state implementation is working great so far. However, I was wondering if you would be able to implement something where all stored inputs could be reset after a certain action? for example for my application, if I upload a new dataset, I would like to have all inputs reset. I dont want the end user to have to refresh the page every time.

---

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [June 2, 2020, 6:21pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/8 "2020-06-02T18:21:04Z")

</div>

Hello @vnguyendc,

Thanks for your feedback!  
I’ve updated the gist. Now you can use `state.clear()` to clear saved values.

---

<div class="post-metadata">

**Author:** ![Adrien\_Treuille](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/adrien_treuille/32/8_2.png) [@Adrien\_Treuille](https://discuss.streamlit.io/u/Adrien_Treuille)\
**Post date:** [June 9, 2020, 10:22pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/9 "2020-06-09T22:22:36Z")

</div>

Wow @okld. This is an awesome implementation! ☀

Programmable state is on our roadmap (after custom components and one-click deploy), and this example will definitely provide inspiration. I can see better why you want to tie the state to a page.

We are not implementing (or even designing) yet, but we’d love to collaborate at some point. In particualr, I’d be curious to know if there are any features _in Streamlit_ which would unblock progress on programmable state for you, or any other thoughts you have.

---

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [June 10, 2020, 5:31pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/10 "2020-06-10T17:31:32Z")

</div>

Hello @Adrien_Treuille,

Thanks for your message, and also to your team for this amazing framework!

I had some small ideas related to widgets’ `key` parameter, and I think it could possibly be useful in designing an easy-to-use session state, and bring even more interaction to Streamlit apps : [Proposal for a more useful key parameter (feat. session state, RESTful app and HTTP query string)](https://discuss.streamlit.io/t/proposal-for-a-more-useful-key-parameter-feat-session-state-restful-app-and-http-query-string/3373)

---

<div class="post-metadata">

**Author:** ![vnguyendc](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/vnguyendc/32/1657_2.png) [@vnguyendc](https://discuss.streamlit.io/u/vnguyendc)\
**Post date:** [July 13, 2020, 3:33pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/12 "2020-07-13T15:33:51Z")

</div>

How would I be able to create custom hash functions for specific objects? For example, I need to define a custom hash function for XGBRegressor in order to store in the state.

---

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [July 13, 2020, 3:48pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/13 "2020-07-13T15:48:45Z")

</div>

That’s something I need to add, indeed. I’ll update the snippet in a few hours. After the modification you’ll be able to pass hash functions to `_get_state({...})` in a dictionary as you do with `@st.cache`.

---

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [July 13, 2020, 5:18pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/14 "2020-07-13T17:18:15Z")

</div>

I’ve updated the gist, you can now pass hash functions as described in my previous reply.

---

<div class="post-metadata">

**Author:** ![vnguyendc](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/vnguyendc/32/1657_2.png) [@vnguyendc](https://discuss.streamlit.io/u/vnguyendc)\
**Post date:** [July 13, 2020, 11:30pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/15 "2020-07-13T23:30:59Z")

</div>

Thank you very helpful. Will give you feedback

---

<div class="post-metadata">

**Author:** ![Charly\_Wargnier](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/charly_wargnier/32/1504_2.png) [@Charly\_Wargnier](https://discuss.streamlit.io/u/Charly_Wargnier)\
**Post date:** [July 24, 2020, 5:43pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/16 "2020-07-24T17:43:32Z")

</div>

Hi @okld I’ve started playing with this afternoon. This truly is an outstanding template that will benefit many, well done! 🙌

Now, having pasted some of my code, I have the following issue:

```
UnboundLocalError: local variable 'st' referenced before assignment
Traceback:
File "c:\users\charly\desktop\multi-sessions-app\venv\lib\site-packages\streamlit\ScriptRunner.py", line 319, in _run_script
    exec(code, module. __dict__ )
File "C:\Users\Charly\Desktop\Multi-sessions-app\st_demo_settings.py", line 400, in <module>
    main()
File "C:\Users\Charly\Desktop\Multi-sessions-app\st_demo_settings.py", line 22, in main
    pages[page](state)
File "C:\Users\Charly\Desktop\Multi-sessions-app\st_demo_settings.py", line 29, in cloud_nlp
    st.title(":chart_with_upwards_trend: Dashboard page")  

```

Would fixing the issue be as easy as re-arranging some blocks/functions within the code?

Thanks,  
Charly

---

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [July 24, 2020, 7:15pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/17 "2020-07-24T19:15:28Z")

</div>

> [@Charly\_Wargnier](#):
>
> Would fixing the issue be as easy as re-arranging some blocks/functions within the code?

Hmm, maybe. It’s just weird that it’s related to `st` though. Could you share what you have so far?

---

<div class="post-metadata">

**Author:** ![Charly\_Wargnier](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/charly_wargnier/32/1504_2.png) [@Charly\_Wargnier](https://discuss.streamlit.io/u/Charly_Wargnier)\
**Post date:** [July 25, 2020, 7:07pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/18 "2020-07-25T19:07:49Z")

</div>

Thanks @okld! I’ve sorted it 👌

I’ve now added 2 of my apps within the same app - SO thrilled by this!

 ![image](https://global.discourse-cdn.com/streamlit/original/2X/c/cddae67bf25e86e6eef2de96ea4e0b683467614e.png)

Out of interest, is it possible to retain the state/results of these 2 added tabs?

Currently, if I trigger results in 1 tab, switch to another tab then come back, these results are gone. Not a deal breaker but it would be super nice to retain their states as well! 🙂

Thanks,  
Charly

---

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [July 25, 2020, 7:23pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/19 "2020-07-25T19:23:50Z")

</div>

If you’re using the page structure implemented in my gist, I pass to each page function a state object as argument into which you can retain any value you want.

I’ll update my post but you need to call that `state.sync()` function only if you’re binding a widget to a state value.

---

<div class="post-metadata">

**Author:** ![Charly\_Wargnier](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/charly_wargnier/32/1504_2.png) [@Charly\_Wargnier](https://discuss.streamlit.io/u/Charly_Wargnier)\
**Post date:** [July 25, 2020, 7:27pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/20 "2020-07-25T19:27:44Z")

</div>

Great, I’ll have a play!

Would that also retain:

#1 - files uploaded via Streamlit’s file uploader?  
#2 - modified dataframes?

Thanks,  
Charly

---

<div class="post-metadata">

**Author:** ![okld](https://sea2.discourse-cdn.com/streamlit/user_avatar/discuss.streamlit.io/okld/32/1829_2.png) [@okld](https://discuss.streamlit.io/u/okld)\
**Post date:** [July 25, 2020, 7:30pm UTC](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074/21 "2020-07-25T19:30:00Z")

</div>

That should work, yes! That’s the main purpose of this gist actually, save some state across pages 😉

[Next page](https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074.md?page=2)
