dcc.Tabs with Dash Pages for Multi-Page App

HI All,

I’m trying to use Dash Pages with a Tabbed page format. I have followed the Plotly Dash documentation and am sure my folder structure is correct: app.py in main folder and three .py files in a pages folder. I’m sure the pages files are correctly format because they are from Adam’s GitHub. Typically:

import dash
from dash import dcc, html
import plotly.express as px

dash.register_page(__name__, path='/')

df = px.data.gapminder()

layout = html.Div(
    [
        dcc.Dropdown([x for x in df.continent.unique()], id='cont-choice', style={'width':'50%'}),
        dcc.Graph(id='line-fig',
                  figure=px.histogram(df, x='continent', y='lifeExp', histfunc='avg'))
    ]
) 

I just cannot seem to get the link between app.py and the pages file to work. Here’s my code:

import dash
from dash import html, dcc, callback, Input, Output

app = dash.Dash(__name__, use_pages=True)

app.layout = html.Div(
    [
        html.Div("Python Multipage App with Dash", style={'fontSize':50, 'textAlign':'center'}),
        
        dcc.Tabs(id='tabs', value='tab-1', children=[
            dcc.Tab(label='Tab one', value='tab-1'),
            dcc.Tab(label='Tab two', value='tab-2')
        ]),

        # content of each page
        dash.page_container    
    ], id='mainDiv'
)

@callback(
    Output('mainDiv', 'children'),
    Input('tabs', 'value')
)
def render_content(tab):
    if tab == 'tab-1':
        return html.Div(dcc.Link(href=dash.page_registry['pages./']['path']), refresh=True)
    if tab == 'tab-2':
        return html.Div(dcc.Link(href=dash.page_registry['pages.pg2']['path']), refresh=True)

if __name__ == "__main__":
    app.run(debug=True, port=8001)

I’m sure I’m missing something. Can anyone help? Thanks!

Hi @MickM

Example 1 using dcc.Tabs

Here’s an example of using dcc.Tabs for navigating a multi-page app. Note that if you update the dcc.Location component in a callback, then Pages will handle updating the content.

import dash
from dash import Dash, dcc, html, Input, Output, callback


app = Dash(__name__, use_pages=True)

app.layout = html.Div(
    [
        dcc.Location(id="url", refresh="callback-nav"),
        html.Div("Python Multipage App with Dash", style={'fontSize': 50, 'textAlign': 'center'}),

        dcc.Tabs(id='tabs', value='tab-1', children=[
            dcc.Tab(label='Tab one', value='tab-1'),
            dcc.Tab(label='Tab two', value='tab-2'),
            dcc.Tab(label='Tab three', value='tab-3')
        ]),
    
        dash.page_container
    ]
)

@callback(
    Output("url", "href"),
    Input("tabs", "value")
)
def update_tab_content(value):  
    if value == "tab-2":
        return "/pg2"         # or get path from the dash.page_registry
     if value == "tab-3":
        return "/pg3"
    return "/"

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

Example 2 using dbc.Nav

Note that you can also do this without any callbacks using the dbc.Nav component from the dash-bootstrap-components library. In this example, the Nav is styled so it’s similar to tabs.

import dash
from dash import html
import dash_bootstrap_components as dbc


app = dash.Dash(__name__, use_pages=True, external_stylesheets=[dbc.themes.BOOTSTRAP])

nav = dbc.Nav(
    [
        dbc.NavItem(dbc.NavLink("Home", active="exact", href="/")),
        dbc.NavItem(dbc.NavLink("Bar Charts", active="exact", href="/bar-charts")), # or get the path from dash.page_registry
        dbc.NavItem(dbc.NavLink("Histograms", active="exact", href="/histograms")),
    ],
    pills=True,
    fill=True,
    className="border my-4"
)

app.layout = dbc.Container(
    [
        html.Div("Multipage App with Dash", className="h2 text-center"),
        nav,
        dash.page_container
    ],
)

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

Thank you very much for you quick reply. Now I understand where I was going wrong. I very much appreciate your help!!

1 Like