✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚾️ It's finally Baseball season! Root for the home team... & Register for our Sports Analytics Webinar!

Simple callback very slow when inside a tab

I’m using the dcc.tabs in a bare-bones app, and a simple callback (using html.Button) responds very slowly when inside a tab. It takes more than a minute after clicking the submit button before the response_div updates. I also experienced this in a more sophisticated app (4 tabs, multiple graphs, etc.) and the response-div took upwards of 5 minutes to update after clicking submit, or just bombed out completely. In both cases, the identical code works fine when not using dcc.tabs. Has anyone else experienced this? Am I missing something obvious? Full code for short app is below. Using dash-core-components-0.28.1.

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


app = dash.Dash()
application = app.server
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True

app.layout = html.Div([
    html.H1('Dash Tabs component demo', style={
            'textAlign': 'center', 'margin': '48px 0', 'fontFamily': 'system-ui'}),
    dcc.Tabs(id="tabs", children=[
        dcc.Tab(label='Tab one', children=[

            html.Div([
                html.H3('Type any number in the box:'),
                dcc.Input(
                    id='number-in',
                    value=1,
                    style={'fontSize':20}
                ),
                html.Button(
                    id='submit-button',
                    n_clicks=0,
                    children='Submit',
                    style={'fontSize':20}
                ),
                html.H4(id="response_div"),
                ],
                className='twelve columns'),

        ]),
        dcc.Tab(label='Tab two', children=[
            html.Div([
                html.H1("This is the content in tab 2"),
                html.P("A graph here would be nice!")
            ])
        ]),
        dcc.Tab(label='Tab three', children=[
            html.H1("This is the content in tab 3"),
            html.P("Insert content here")
        ]),
    ]),
])

# ------------------------#
# CALLBACKS               #
# ------------------------#


# Call topic number
@app.callback(
    Output('response_div', 'children'),
    [Input('submit-button', 'n_clicks')],
    [State('number-in', 'value')])
def output(n_clicks, number):
    return  'The number you have picked is: {}'.format(number),


if __name__ == '__main__':
    application.run(debug=True, port=8020)

One issue might be that you are loading the app’s content on page load from all of the tabs on page load, rather than just the currently selected tab. This will trigger all of the callbacks from each tab rather than just the callbacks from the currently selected tab. This is a pretty subtle point about tabs, see the bullet points on “This method has a couple of drawbacks” under the “Method 2” part of the tabs docs: https://dash.plot.ly/dash-core-components/tabs
To get around this, you can use the Method 1 as documented on that page.

Thanks @chriddyp – this solution worked successfully for me, with the important addition of the suppression statement app.config['suppress_callback_exceptions'] = True
(see Chris’ response to this thread: dcc.Tabs: Filling tabs with dynamic content. How to organize the callbacks?)

Given this solution, I don’t see why anyone would want to use dcc.tabs Method #2 ( Content as Tab Children) – it basically makes it impossible to have any dynamic content in your tabbed app. That will be a dealbreaker for most of us, I think.

Anyway, here is my updated code, with Chris’ solution applied, in case others have the same problem.

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

app = dash.Dash()

app.config['suppress_callback_exceptions'] = True

app.layout = html.Div([
    dcc.Tabs(id="tabs", value='tab-1', children=[
        dcc.Tab(label='Tab one', value='tab-1'),
        dcc.Tab(label='Tab two', value='tab-2'),
    ]),
    html.Div(id='tabs-content')
])

@app.callback(Output('tabs-content', 'children'),
              [Input('tabs', 'value')])
def render_content(tab):
    if tab == 'tab-1':
        return html.Div([
            html.H3('Type any response in the box:'),
            dcc.Input(
                id='number-in',
                value=1,
                style={'fontSize':20}
            ),
            html.Button(
                id='submit-button',
                n_clicks=0,
                children='Submit',
                style={'fontSize':20}
            ),
            html.H4(id="response_div"),
            ],
            className='twelve columns')
    elif tab == 'tab-2':
        return html.Div([
            html.H3('Tab content 2')
        ])



# Call back number
@app.callback(
    Output('response_div', 'children'),
    [Input('submit-button', 'n_clicks')],
    [State('number-in', 'value')])
def output(n_clicks, number):
    return  'The response you have picked is: {}'.format(number),



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

I personally agree, which is why I’ve made it the secondary option and included the disclaimers below the example. Perhaps the disclaimers could be more obvious. However, some folks find putting content into a callback tedious, see for example: https://github.com/plotly/dash-core-components/pull/213#issuecomment-400090475

1 Like

I have successfully implemented the solution you suggested, and posted my code in another thread in case others might learn from it.

1 Like

I would like this twice if I could. Ran into a similar dead end myself with a multi page app, following the Vanguard example. Thank you for going through the pain, reading through the docs, and sharing your code.