Learn how to use Dash Bio for next-gen sequencing & quality control. 🧬Register for the Oct 27 webinar.

Structuring a multi-page app

So I’d like to build a multi-page app where each page’s code is saved in its own file. I’ve attempted to follow the demo provided in the documentation, but it failed for me: URL Routing and Multiple Apps | Dash for Python Documentation | Plotly

Is this demo 100%? It seems like maybe the app.py example is missing something. I’m attempting to troubleshoot on my own, but wanted to know if others have successfully replicated this demo. Thanks!

1 Like

I modified the demo provided in the Dash documentation listed above, and successfully got it to work. It was necessary to include all of the callbacks in the primary file (app.py) – not sure if this is a best practice, but I couldn’t get it to work otherwise.

File structure is as follows:

main.py
pages folder:
— empty file named “_ _ init _ _.py”
— page_1.py
— page_2.py
— index.py

main.py

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from pages import index
from pages import page_1
from pages import page_2

print(dcc.__version__) # 0.6.0 or above is required

app = dash.Dash()

app.config.suppress_callback_exceptions = True

app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content')
])

# Page 1 callback
@app.callback(dash.dependencies.Output('page-1-content', 'children'),
              [dash.dependencies.Input('page-1-dropdown', 'value')])
def page_1_dropdown(value):
    return 'You have selected "{}"'.format(value)

# Page 2
@app.callback(Output('page-2-content', 'children'),
              [Input('page-2-radios', 'value')])
def page_2_radios(value):
    return 'You have selected "{}"'.format(value)

# Index Page callback
@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/page-1':
        return page_1.page_1_layout
    elif pathname == '/page-2':
        return page_2.page_2_layout
    else:
        return '404'

app.css.append_css({
    'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'
})

if __name__ == '__main__':
    app.run_server(debug=True)

page_1.py

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

page_1_layout = html.Div([
    html.H1('Page 1'),
    dcc.Dropdown(
        id='page-1-dropdown',
        options=[{'label': i, 'value': i} for i in ['LA', 'NYC', 'MTL']],
        value='LA'
    ),
    html.Div(id='page-1-content'),
    html.Br(),
    dcc.Link('Go to Page 2', href='/page-2'),
    html.Br(),
    dcc.Link('Go back to home', href='/'),

])

page_2.py

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

page_2_layout = html.Div([
    html.H1('Page 2'),
    dcc.RadioItems(
        id='page-2-radios',
        options=[{'label': i, 'value': i} for i in ['Orange', 'Blue', 'Red']],
        value='Orange'
    ),
    html.Div(id='page-2-content'),
    html.Br(),
    dcc.Link('Go to Page 1', href='/page-1'),
    html.Br(),
    dcc.Link('Go back to home', href='/')
])

index.py

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

index_page = html.Div([
    dcc.Link('Go to Page 1', href='/page-1'),
    html.Br(),
    dcc.Link('Go to Page 2', href='/page-2'),
])
6 Likes

https://yadi.sk/d/WZURkNcP3VawdW

here is multipage app organized 1page=1file callbacks are placed inside this files + custom css. it can be usefull as template to start with

1 Like

Thank you for this! How do you serve it? gunicorn? Does it work with multiple workers?

I use it with nginx as proxy + gunicorn, but nginx is not necessary. Yes it works with multiple workers.

Hi Roman,
THx for sharing.
Is the link still active?
It does not work for me.

updated link https://yadi.sk/d/JnM7BvKbJp3EdA

2 Likes

Thank you very much @roman !

Is the link still working? Does not work for me.

When I try to run this code I get a black page with 404 and no error.

I have tried both approaches from the tutorial and also just see 404. So somehow the pathnames do not get passed correctly?

1 Like

Hii, Actually It is returning 404 from Main.py file. You need to change it with index.index_page ,
And problem will get solved

It looks like this app only separates layouts into different files. But only the main file is creating an app instance. Does that mean all the callbacks have to be included in the main file?

Yes @mail8, you’re right !

For more info checkout this doc

I created a multi-page app where the different callbacks are collected in functions. So, only the callbacks relevant for a certain view are in the same file with the layout information. In principle these could be split into layout and callback files as well:

If you are interested.