Using plotly dash + pages + dcc.Tab - page referencing not working

Hi there,
am new to dash and pages and think there is something fundamental I have not understood yet.

I am trying to build an app struction with several tabs, whereas I can populate the tabs with content contained in seperate .py files. I want my app.py to be as sleek as possible. My code produces the app and the tabs, but each tab displays the error “Page ‘page1’ not found” respectively because the entry in the registry is not found.

Could you help me figure this out?

Below is my code:

app.py

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

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

app.layout = html.Div([
    html.H1('Dashboard'),
    
    dcc.Tabs(id="tabs", value='page1', children=[
        dcc.Tab(label='Tab 1', value='page1'),
        dcc.Tab(label='Tab 2', value='page2'),
        dcc.Tab(label='Tab 3', value='page3'),
    ]),

    dash.page_container
])

@callback(
    Output(dash.page_container, 'children'),
    Input('tabs', 'value')
)

def render_content(tab):
    if tab in dash.page_registry:
        return dash.page_registry[tab]['layout']
    else:
        return html.Div(f"Page '{tab}' not found")
    
def render_content(tab):              
       return dash.page_registry[tab]['layout']

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

page1.py (under folder ‘pages/’)

import dash
from dash import html, dcc

dash.register_page( __name__ , path='/', name='Page 1')

layout = html.Div([
    html.H1('Page 1'),
    html.Div('This is the content of page 1'),
    dcc.Input(id='input-box'),
    html.Div(id='output-text')
])

page2.py (under folder ‘pages/’)

import dash
from dash import html, dcc

dash.register_page( __name__, name='Page 2')

layout = html.Div([
    html.H1('Page 2'),
    html.Div('This is the content of page 2'),
    dcc.Input(id='input-box'),
    html.Div(id='output-text')
])
``

Hi @plotplot ,

check the docs: https://dash.plotly.com/dash-core-components/tabs

In your case it should be something like:

from pages import page1, page2

layout = [
    html.Div(
        [
            html.H1("Dash Tabs component demo"),
            dcc.Tabs(
                id="tabs-example-graph",
                value="tab-1-example-graph",
                children=[
                    dcc.Tab(label="Tab One", value="tab-1-example-graph"),
                    dcc.Tab(label="Tab Two", value="tab-2-example-graph"),
                ],
            ),
            html.Div(id="tabs-content-example-graph"),
        ]
    ),
]

@callback(
    Output("tabs-content-example-graph", "children"),
    Input("tabs-example-graph", "value"),
)
def render_content(tab):
    if tab == "tab-1-example-graph":
        return page1.layout
    elif tab == "tab-2-example-graph":
        return page2.layout

Im not sure where your pages are, if they are on the same level as app.py then its just

import page1
import page2

If they are in a pages folder then

from pages import page1, page2

Thanks @dashamateur for your reply and suggestion.

What I am trying to make work is a construction using “Dash Pages” which uses the corresponding dash.page_container to pull content from the individual .py files into the main app. (yes, pages, are under folder ‘pages/’

I had checked the documentation on tabs which you mention, but it does not talk about how to use it in combination with the Dash Pages constructor, and vice versa for the Dash Pages documentation.

Or is this combination not meant to be?

Oh i see. For that you need to change url. Try this:

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

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

app.layout = html.Div([
    html.H1('Dashboard'),
    
    dcc.Tabs(id="tabs", value='page1', children=[
        dcc.Tab(label='Tab 1', value='page1'),
        dcc.Tab(label='Tab 2', value='page2'),
        dcc.Tab(label='Tab 3', value='page3'),
    ]),

    dash.page_container,
    dcc.Location(id="url"),
])

@callback(
    Output("url", "pathname"),
    Input("tabs", "value"),
)
def render_content(tab):
    return f"/{tab}"

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

I haven’t tried this exactly but the callback works for me.

EDIT:
I revised your posted code and caught an error in both your pages. You have to define proper paths for them. Lets say you are at url http://127.0.0.1:8050/. This means the page you are displaying has path “/”. In your case page1 which is fine, but you can’t pass value of tab1 as page1 but as /. The second page doesn’t have a path defined.

This code works fine, but the catch is after you pass a new url a new page is loaded which means it will reset tabs to original state.

app.py

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

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

app.layout = html.Div(
    [
        html.H1("Dashboard"),
        dcc.Tabs(
            id="tabs",
            children=[
                dcc.Tab(label="Tab1", value="page1"),
                dcc.Tab(label="Tab2", value="page2"),
                dcc.Tab(label="Tab3", value="page3"),
            ],
        ),
        dash.page_container,
        dcc.Location(id="url"),
    ]
)


@callback(
    Output("url", "pathname"),
    Output("tabs", "value"),
    Input("tabs", "value"),
)
def render_content(tab):
    if tab.startswith("page"):
        print(tab)
        return f"/{tab}", tab

    else:
        return dash.no_update


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

pages/home.py

import dash
from dash import html, dcc

dash.register_page(__name__, path="/", name="Home")

layout = html.Div(
    [
        html.H1("Home"),
        html.Div("This is the content of home page. Pick a tab!"),
    ]
)

pages/page1.py

import dash
from dash import html, dcc

dash.register_page(__name__, path="/page1", name="Page 1")

layout = html.Div(
    [
        html.H1("Page 1"),
        html.Div("This is the content of page 1"),
        dcc.Input(id="input-box"),
        html.Div(id="output-text"),
    ]
)

pages/page2.py

import dash
from dash import html, dcc

dash.register_page(__name__, path="/page2", name="Page 2")
layout = html.Div(
    [
        html.H1("Page 2"),
        html.Div("This is the content of page 2"),
        dcc.Input(id="input-box"),
        html.Div(id="output-text"),
    ]
)

Maybe replace tabs with links as it is displayed in docs or check out my approach from the original response. If you are really set on tabs and page_container check out this debate:
https://community.plotly.com/t/combining-dcc-tabs-and-dcc-location/27831

Personally i think it’s just too much hastle. Just define layouts inside each Tab and be happy :smiley:

1 Like

With this version, indeed there is no callback error, but the only content displayed is the string “/page1”. How would I need to formulate the return statement to receive the whole content of page1.py?

Can you please copy your current code? Did you correct page1 and page2?

Thanks for the update edit above concering the version Dash Pages - it works

I’ll follow your advice to use the more straight forward version without Dash Pages. I’ll post my code below for those looking up this thread later on.

Many thanks for your help!

app.py

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

from pages import page1, page2, page3

app = dash.Dash()

app.layout = html.Div([
            
        html.H1("Dashboard"),
            dcc.Tabs(id="tabs-example-graph",value="tab-1",
                children=[
                    dcc.Tab(label="Tab 1", value="tab-1"),
                    dcc.Tab(label="Tab 2", value="tab-2"),
                    dcc.Tab(label="Tab 3", value="tab-3"),
                ],
            ),
            html.Div(id="tabs-content-example-graph"),
        ])


@callback(
    Output("tabs-content-example-graph", "children"),
    Input("tabs-example-graph", "value"),
)
def render_content(tab):
    if tab == "tab-1":
        return page1.layout
    elif tab == "tab-2":
        return page2.layout
    elif tab == "tab-3":
        return page3.layout
    
if __name__ == '__main__':
    app.run_server(debug=True, port='8052')

pages/page1.py

import dash
from dash import html, dcc

layout = html.Div(
    [
        html.H1("Page 1"),
        html.Div("This is the content of page 1"),
        dcc.Input(id="input-box"),
        html.Div(id="output-text"),
    ]
)

pages/page2.py

import dash
from dash import html, dcc

layout = html.Div(
    [
        html.H1("Page 2"),
        html.Div("This is the content of page 2"),
        dcc.Input(id="input-box"),
        html.Div(id="output-text"),
    ]
)

pages/page3.py
—> idem page 2.