Dash multipage error: Cannot read properties of undefined (reading 'title')

Hi,

I have made a Dash Multipage App and I am facing the following issue. After login the navbar with the links show but the content will only show after I click a link (like the link dashboard). It doesn’t go to the homepage (path=‘’/). But after clicking on a link the app works fine.

I am using python version 3.12, dash version 2.16.0 and macOS 14.4.

The error message I receive is:

Cannot read properties of undefined (reading ‘title’)

(This error originated from the built-in JavaScript code that runs Dash apps. Click to see the full stack trace or open your browser’s console.)
TypeError: Cannot read properties of undefined (reading ‘title’)

at ns.bb8c1797de374dfadf83d372d1a7e859 (http://127.0.0.1:8050/:51:43)

at _callee2$ (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_16_0m1709359683.dev.js:604:72)

at tryCatch (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_16_0m1709359683.dev.js:425:1062)

at Generator.<anonymous> (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_16_0m1709359683.dev.js:425:3010)

at Generator.next (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_16_0m1709359683.dev.js:425:1699)

at asyncGeneratorStep (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_16_0m1709359683.dev.js:431:103)

at _next (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_16_0m1709359683.dev.js:432:194)

at http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_16_0m1709359683.dev.js:432:364

at new Promise (<anonymous>)

at http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_16_0m1709359683.dev.js:432:97

I tried to remove the app.tilte and the title in the register_page, but without any result. I get the same error in both Chrome as Safari.

Can someone please help me?

Here is my app.py

import dash

# external JavaScript files for Firebase authentication / to create a Firebase app
external_scripts = [
    {'src': '/__/firebase/8.9.1/firebase-app.js'},
    {'src': '/__/firebase/8.9.1/firebase-analytics.js'},
    {'src': '/__/firebase/init.js'}
]


# meta_tags are required for the app layout to be mobile responsive
app = dash.Dash(__name__, suppress_callback_exceptions=True, use_pages=True, external_scripts=external_scripts,
                meta_tags=[{'name': 'viewport',
                            'content': 'width=device-width, initial-scale=1.0'}]
                )

# Workaround/hack for broken gunicorn
server = app.server

# Set app title
app.title = 'My App'

app.index_string = '''
<!DOCTYPE html>
<html>
    <head>
        {%metas%}
        <title>{%title%}</title>
        {%favicon%}
        {%css%}
    </head>
    <body>
        {%app_entry%}
        <footer>
            {%config%}
            {%scripts%}
            {%renderer%}
        </footer>
    </body>
</html>
'''

My index.py:

import dash
from dash import dcc, html, Input, Output, State
import dash_bootstrap_components as dbc

import sys
sys.path.append('scripts/')

# Connect to main app.py file
from app import *

# Import layouts for authentication and authorization flow
from layout_auth import layout_log_in


# Import settings
from scripts.libraries import color1

# Import authentication callbacks
import auth_callbacks

# Import callbacks for the dashbaord
import callbacks

# Initialize firebase_admin
import firebase_admin
default_app = firebase_admin.initialize_app()

# Make the overall layout, which is rendered by the display_page() callback
app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content', className='content'),  
    dcc.Store(id='email', storage_type='session'),
    html.Div(id='dummy_div'),
    dcc.Store(id='navbar_logo', storage_type='session'),
    dcc.Store(id='text-memory', storage_type='session'),
    dcc.Store(id='filtered_df-cache', storage_type='session'), 
    dcc.Store(id='dict-cache', storage_type='session'),
    dcc.Store(id='summary1-cache', storage_type='memory'),
    dcc.Store(id='summary2-cache', storage_type='memory'),
    html.Div(id='logout') 
    ])

########################################### render content with authorization ###############################################
@app.callback(
    [Output('page-content', 'children'),
     Output('navbar_logo', 'data')],
    [Input('url', 'pathname')],
    [State('email', 'data')]
    )
def display_page(pathname, email):
    from authorization import access_restricted_content, check_access
    from layout_navbar import navbar, navbar_logo_only
    LOGO = navbar_logo_only()[1]

    if pathname == '/login': 
        layout = html.Div([
            html.Br(style={"line-height": "5"}),
            layout_log_in
        ])
        return [layout, LOGO] 

    else:
        # verify token validity
        access_restricted_content()
        #check access
        if email:
            active_dashboards, active_subscription_plans, subscriptions = check_access(email)
            if pathname == '/' or pathname == dash.page_registry['pages.dashboard']['path'] or pathname == dash.page_registry['pages.profile']['path']:
                dashboard_navbar = navbar(active_dashboards, active_subscription_plans)[0]
            else:
                # dashboard_navbar = navbar_logo_only()[0]
                dashboard_navbar = []

            if pathname == dash.page_registry['pages.dashboard']['path'] or pathname == dash.page_registry['pages.sentiments']['path']:
                from pages.side_bar_dashboard import sidebar
                sidebar_ = sidebar()
            else:
                sidebar_ = html.Div('No Sidebar', style={"border":"2px black solid", 'width': '100%'})

            layout = html.Div([
                dashboard_navbar,
                html.Br(style={"line-height": "5"}),
                # content of each page
                dbc.Row(
                    [
                        dbc.Col(
                            [
                                sidebar_,
                                html.Br(),
                            ], xs=10, sm=10, md=3, lg=2, xl=2, xxl=2),


                        dbc.Col(
                            [
                                dash.page_container
                            ], xs=12, sm=12, md=9, lg=10, xl=10, xxl=10),

                    ]
                )
            ])
            return [layout, LOGO]
        else:
            return [dcc.Location(pathname= '/login', id='url_log_in', refresh=True), LOGO]
  

######################## Navbar callbacks ##############################
# Toggle the collapse on small screens (navbar)
@app.callback(
    Output("navbar-collapse", "is_open"),
    [Input("navbar-toggler", "n_clicks")],
    [State("navbar-collapse", "is_open")],
)
def toggle_navbar_collapse(n, is_open):
    if n:
        return not is_open
    return is_open

# Callback for logo ()
@app.callback(
    Output('logo', 'children'),
    Input('dummy_div', 'children'), #This is what ensures that page load triggers callback
    State('navbar_logo', 'data')
    )
def show_logo(trigger, LOGO):
    img = LOGO
    from base64 import b64encode
    encoded_image = b64encode(open(img, 'rb').read())
    return html.Img(src='data:image/svg+xml;base64,{}'.format(encoded_image.decode()), height='75', style={'width': '100%'})
    
if __name__ == '__main__':
    app.run_server(debug=True)

My home.py:

from dash import dcc, html, register_page
import dash_bootstrap_components as dbc

register_page(__name__, 
              title = 'My App', 
              name='home', 
              path='/')

layout = html.Div([ 
    # dcc.Location(id='url_home', refresh=False),
    html.Br(),
    html.Div(html.H1('home')),
    dbc.Container([
        dbc.Row(
            [
                html.Br(),
                html.H3('Welcome!')
            ], justify="center")
        ], fluid=True)
    ])

Maybe not related, but try updating dash. Version 2.16.0 had some issues, mainly with other dash libraries. Maybe this applies to the external_stylesheets you import too.

1 Like

Hi @VijayB

To start try removing this ,because it’s set by Pages


# Set app title
app.title = 'My App'

app.index_string = '''
<!DOCTYPE html>
<html>
    <head>
        {%metas%}
        <title>{%title%}</title>
        {%favicon%}
        {%css%}
    </head>
    <body>
        {%app_entry%}
        <footer>
            {%config%}
            {%scripts%}
            {%renderer%}
        </footer>
    </body>
</html>
'''

Also, it’s not necessary to set this in the app constructor because it’s been the default since about Dash 2.6 (but this isn’t the problem)

 meta_tags=[{'name': 'viewport',
                            'content': 'width=device-width, initial-scale=1.0'}

Typically when you use Pages, you don’t also have an index.py Is there a reason you don’t just combing app.py and index.py?

2 Likes

Hi @AnnMarieW, thank you for your reply. I have done what you have suggested (merged the index.py and app.py into app.py and removed the app.title, app.index_string, and meta_tags. However, the error remained.

I studied it further and it seems that when I try to render the page (to catch the pathname) then it will give me that error message. In my app I want to catch the pathmame and another variable (email).

What am I doing wrong? I want to pass the pathname and the email, it did work previously without using pages (the old way).

Thanks in advance!

Vijay

Working app.py:

import dash
from dash import Dash, dcc, html, Input, Output, State, callback
import dash_bootstrap_components as dbc

# Import settings
from scripts.libraries import color1

# Initialize firebase_admin
import firebase_admin
default_app = firebase_admin.initialize_app()

# external JavaScript files for Firebase authentication / to create a Firebase app
external_scripts = [
    {'src': '/__/firebase/8.9.1/firebase-app.js'},
    {'src': '/__/firebase/8.9.1/firebase-analytics.js'},
    {'src': '/__/firebase/init.js'}
]

app = Dash(__name__, suppress_callback_exceptions=True, use_pages=True, external_scripts=external_scripts)

from layout_navbar import navbar
navbar_, LOGO = navbar()

app.layout = html.Div([
    navbar_,
    html.Br(style={"line-height": "5"}),
    dash.page_container
])

######################## Navbar callbacks ##############################
# Toggle the collapse on small screens (navbar)
@callback(
    Output("navbar-collapse", "is_open"),
    [Input("navbar-toggler", "n_clicks")],
    [State("navbar-collapse", "is_open")],
)
def toggle_navbar_collapse(n, is_open):
    if n:
        return not is_open
    return is_open
    
if __name__ == '__main__':
    app.run_server(debug=True)

Error with this app.py:

import dash
from dash import Dash, dcc, html, Input, Output, State, callback
import dash_bootstrap_components as dbc

# Initialize firebase_admin
import firebase_admin
default_app = firebase_admin.initialize_app()

# external JavaScript files for Firebase authentication / to create a Firebase app
external_scripts = [
    {'src': '/__/firebase/8.9.1/firebase-app.js'},
    {'src': '/__/firebase/8.9.1/firebase-analytics.js'},
    {'src': '/__/firebase/init.js'}
]

app = Dash(__name__, suppress_callback_exceptions=True, use_pages=True, external_scripts=external_scripts)

########################################### render content  ###############################################
from layout_navbar import navbar
navbar_, LOGO = navbar()

@callback(
    Output('page-content', 'children'),
    [Input('url', 'pathname')],
    )  
def display_page(pathname):
        layout = html.Div([
            navbar_,
            html.Br(style={"line-height": "5"}),
            dash.page_container
        ])
        
        return layout

# Make the overall layout, which is rendered by the display_page() callback
app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content', className='content')
    ])

######################## Navbar callbacks ##############################
# Toggle the collapse on small screens (navbar)
@callback(
    Output("navbar-collapse", "is_open"),
    [Input("navbar-toggler", "n_clicks")],
    [State("navbar-collapse", "is_open")],
)
def toggle_navbar_collapse(n, is_open):
    if n:
        return not is_open
    return is_open
    
if __name__ == '__main__':
    app.run_server(debug=True)

@VijayB
There is not enough info here to help. Can you make a complete minimal example that I can run that reproduces this issue?

Could you remove anything that’s unnecessary to reproduce the error? For example can you replicate the error without using firebase? I’m unfamiliar with that package.

Hi @AnnMarieW

I have uploaded the minimal reproducable example at GitHub: MRE

Hi @VijayB

Thanks for the GitHub MRE - that was very helpful!

Try replacing your entire app.py with this:


import dash
from dash import Dash, dcc, html, Input, Output, State, callback
from layout_navbar import navbar

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

app.layout = html.Div([
    navbar(),
    html.Br(style={"line-height": "5"}),
    dash.page_container
])

@callback(
    Output("navbar-collapse", "is_open"),
    [Input("navbar-toggler", "n_clicks")],
    [State("navbar-collapse", "is_open")],
)
def toggle_navbar_collapse(n, is_open):
    if n:
        return not is_open
    return is_open

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

Note that Pages includes the URL routing callback “under the hood” so you don’t need to include that yourself. Be sure to check out the dash docs for all the info

Also, you can see a bunch of minimal examples here:

2 Likes

@AnnMarieW Thank you!

1 Like