Multi-page apps

Is there any support for apps that have multiple pages? My use case is a 3-part workflow that I’d like to separate into three separate views with state carried across them.

Stages generally are: 1- overview of data and category specification, 2- model refinement with active learning, and 3- overview of trained model results).

Is this use case out of scope for streamlit, something that’s possible now, or something that’s planned?

Thanks

Sounds interesting for me as well.

Hi. This is in-scope and there are a number of ways to achieve it now.

  1. You can store this part of the state (i.e. which page you’re in) in a selectbox as shown in the Udacity demo.
  2. You can use @tvst’s SessionState hack but please note that this method is undocumented and may not work in the future.
  3. You can store state as a mutable object in the cache using the ignore_hash=True kwarg. (Here’s an example of how to do that.)
  4. You can wait until we have a proper SessionState implementation but that won’t be until the end of November, according to current plans.

Please followup if you have any questions!

Hi @timforr

When you have developed a multipage app please share you ideas, best practices or code. I’m very interested in the subject.

My multipage app is https://github.com/MarcSkovMadsen/awesome-streamlit and https://awesome-streamlit.org/ for inspiration.

Hi guys,

is anyone working on solutions / Feature for multi-page or is there already
some awesome way to do it in Streamlit?

I am waiting for something nice like:

st.nav({
page1: start.py,
page2: home.py
}}

all in the same directory as index.py. Thus displaying the
pages in the header or Sidebar

st.sidenav({
page1: about.py,
page2: settings.py
}}

Looking forward to this feature!

There are ways of doing this based on a modification of the SessionState gist and the rerun() gist. My method uses a stack of state objects to provide the navigation. I built this handling into a “StreamlitPage” base class and subclass all my pages from it. I use a long if … elif … to do the dispatch (as the navigation varies with each page) but the dict method could be made to work as well.

I shared the modified SessionState (it’s attached to one of the Issues on Github). I haven’t shared the base class as: a) it will need rework once Streamlit finalize their approach; b) there are still some glitches which should be fixed when we get to upgrading to the 0.54+ releases and the latest SessionState implementation; and c) there are also several other application services (configuration, logging) built into the class that are specific to our environment and I need to refactor to take those out.

HI Marc, your application is really good and the idea of using Selectbox for having displays for multiple displays is inspiring. thanks for sharing.

I’ve build pages into classes and I’ve made a manager than verify choice options with ‘st.ratio’, I’ve created a dictionary and return the options.

look the code:

import streamlit as st
from posting import Posting
from tables import Tables


pages = {'tables':Tables,'posting':Posting}

choice = st.sidebar.radio("Choice your page: ",tuple(pages.keys()))

pages[choice]()

I’ve build the page into the ‘init’ classes

Can you provide a link to your classes to see what they do? Do you by any chance expose streamlit methods from your class __init__s?

Another option I have used is to leverage the file system to save the stateful aspect of your app and save it / load it.

import os
import pickle
import streamlit as st

class State:
    def __init__(self, path='state.pickle', default_state_class=dict):
        self.path = path
        self.default_state_class = default_state_class

    def load(self):
        if os.path.exists(self.path):
            with open(self.path, 'rb') as inf:
                self.state = pickle.load(inf)
        else:
            self.state = self.default_state_class()

    def get_state(self):
        return self.state

    def save(self):
        with open(self.path, 'wb') as outf:
            pickle.dump(self.state, outf)

def rerun():
    raise st.script_runner.RerunException(st.script_request_queue.RerunData(None))

def app():
    store = State()
    store.load()

    name = store.get_state().get('name', None)
    if name:
        st.text(f'Hello {name}')
    else:
        st.text(f'Please enter your name')
        name_input = st.text_input('your name')
        name = name_input

        if name != '':
            store.get_state()['name'] = name

        store.save()
        next_page = st.button('Next page')
        if next_page:
            rerun()

if __name__ == "__main__":
    app()

I have followed the example from @Vojaganto so far and works very well.However, I ran into the problem that Reload ® does not take into account changes made on subpages. What is the reason for it and is there any way to fix it?

Hello @chris_klose, you’d need some session state to keep changes between subpages.
I’ve made a topic on this covering specifically the case of a multi-page app with a settings page here:

Hey Peeps,
I don’t want the values added in the app itself to be saved.
My problem is, that if I change the code itself and rerun (by pressing R) the app, the changes made in the code do not become active. I have to start the app again (Streamlit run app.py) so they get active.
So I’d like to know if there is a workaround to solve that. Any ideas?

Hi! Check my framework out:

Hi @timforr ,
Here’s one Streamlit multi-page implementation: