Loading spinner for graph without an "Input"

I have a dashboard with several graphs and no callbacks and inputs. When the graphs load they are empty for a short time which can be confusing for users (this is especially noticeable with slower devices). There are many examples of using dcc.Loading with some Input (dropdown, text field, etc.); however, I don’t use any Inputs and callbacks. Is there any way to provide a loading indicator for graphs when user enters the website in this case? There are some similar threads and questions (like omitting Input argument to callback); however they work poorly and show errors in the Dash debug error window.

# -*- coding: utf-8 -*-
from dash import Dash, dcc, html
import time

from dash.dependencies import Input, Output

app = Dash(__name__)

app.layout = html.Div(
    children=[
        html.H3("Edit text input to see loading state"),
        dcc.Input(id="loading-input-1", value='Input triggers local spinner'),
        dcc.Loading(
            id="loading-1",
            type="default",
            children=html.Div(id="loading-output-1")
        ),
        html.Div(
            [
                dcc.Input(id="loading-input-2", value='Input triggers nested spinner'),
                dcc.Loading(
                    id="loading-2",
                    children=[html.Div([html.Div(id="loading-output-2")])],
                    type="circle",
                )
            ]
        ),
    ],
)


@app.callback(Output("loading-output-1", "children"), Input("loading-input-1", "value"))
def input_triggers_spinner(value):
    time.sleep(1)
    return value


@app.callback(Output("loading-output-2", "children"), Input("loading-input-2", "value"))
def input_triggers_nested(value):
    time.sleep(1)
    return value


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

Just wrap your graphs into dcc.Loading. You could see here, these two callbacks are just for the demonstration, didn’t change anything and can be removed.

Or you could consider listening to the component’s loading_state property to trigger the callback.

And, please follow this below.

Sorry for not positing the MRE. Unfortunately I still don’t get your advice abut dcc.Loading.

Let’s say that I have the following:

from dash import Dash, dcc, html
import time

from plotly import express as px

app = Dash(__name__)

def make_some_graph():
    return dcc.Graph(figure=px.scatter(px.data.tips(), x="total_bill", y="tip", facet_col="sex", height=400))

app.layout = html.Div(
    children=[
        html.P('First graph'),
        make_some_graph(),
        html.P('Second graph'),
        make_some_graph(),
        html.P('Third graph'),
        make_some_graph(),
        html.P('Fourth graph'),
        make_some_graph(),
        html.P('Fifth graph'),
        make_some_graph(),
        html.P('Sixth graph'),
        make_some_graph(),    
    ],
)

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

If I run the app there is a brief state between the ‘Loading…’ screen and the final result, when I see something like this:

This state is rather short, but the larger my app grows and the less performant the device is the worse it gets.

Following your advice:

Just wrap your graphs into dcc.Loading. You could see here, these two callbacks are just for the demonstration, didn’t change anything and can be removed.

I modified the layout to the following:

app.layout = html.Div(
    children=[
        html.P('First graph'),
        dcc.Loading(children=[make_some_graph()]),
        html.P('Second graph'),
        dcc.Loading(children=[make_some_graph()]),
        html.P('Third graph'),
        dcc.Loading(children=[make_some_graph()]),
        html.P('Fourth graph'),
        dcc.Loading(children=[make_some_graph()]),
        html.P('Fifth graph'),
        dcc.Loading(children=[make_some_graph()]),
        html.P('Sixth graph'),
        dcc.Loading(children=[make_some_graph()]),
    ],
)

which yields no difference. I can still se the partially loaded page without any spinners/progress indicators.

from dash import Dash, dcc, html
from dash.dependencies import Input, Output

from plotly import express as px

app = Dash(__name__)

app.layout = html.Div([
    dcc.Loading([
        my_trigger := html.Div(), my_graph :=
        dcc.Graph(figure=px.scatter(px.data.tips(),
                                    x="total_bill",
                                    y="tip",
                                    facet_col="sex",
                                    height=400))
    ])
])


@app.callback(Output(my_trigger, 'children'), Input(my_graph, 'loading_state'))
def on_loading(state):
    return


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