Loading Bar Changing Tabs

I have computationally expensive callbacks when I switch between tabs in my Dash app. The “content as callback” method is ideal for my use case. However, when a user switches tabs, I want to clear the current content and display a loading symbol so it is obvious to the user things are in progress.

I currently have my callback setup like:

            Output(ids.TABS_CONTENT, "children"),
            Output(ids.TABS_LOADING_OUTPUT, "children")
        Input(ids.TABS, "value"),
    def _render_tab(tab):
        div = get_div_content()
        return div, tab

The behavior I see out of this is a loading symbol when I first load the page to get TABS_CONTENT. If I change tabs, it just waits until a new output is available for TABS_CONTENT and I never see the loading symbol again.

My goal is when the user changes tabs, TABS_CONTENT is cleared, then TABS_LOADING_OUTPUT showing the loading symbol, and finally TABS_CONTENT is populated with new information once the callback is finished. I don’t have errors to troubleshoot so I’m struggling to solve this problem, any help is greatly appreciated.

Is there a way to restructure my callback to do that?

Hi @trevbot welcome to the forums.

You might use a background callback for this.

An example:

from dash import Dash, DiskcacheManager, Input, Output, html, dcc, callback
import time
import diskcache        # Diskcache for non-production apps when developing locally

cache = diskcache.Cache("./cache")
background_callback_manager = DiskcacheManager(cache)

app = Dash(__name__, background_callback_manager=background_callback_manager)

app.layout = html.Div(
        html.Button(id="btn", children="change"),
        html.Div(id='output', children=["Button not clicked"]),
                children=["just some initial text"],
                    'visibility': 'hidden',

        Output("output", "children"),
        Output("while_loading", "children")
    inputs=Input("btn", "n_clicks"),
        (Output("output", "children"), "", ""),
        (Output("while_loading", "style"), {}, {'visibility': 'hidden'}),
def update_clicks(n_clicks):
    sec = 3
    return [f"your expensive calculation took {sec} seconds"], 'dummy_string'

if __name__ == "__main__":