Advanced Callbacks

To get the most out of this page, make sure you've read about Basic Callbacks in the Dash Fundamentals.

Catching Errors with PreventUpdate

In certain situations, you don't want to update the callback output. You can achieve this by throwing a PreventUpdate error in the callback function.

using Dash

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash(external_stylesheets=external_stylesheets)

app.layout = html_div() do
    html_button("Click here to see the content", id="show-secret"),
    html_div(id="body-div")
end

callback!(
    app,
    Output("body-div", "children"),
    Input("show-secret", "n_clicks")
) do n_clicks
    if isnothing(n_clicks)
        throw(PreventUpdate())
    end
    return "Elephants are the only animal that can't jump"
end

run_server(app, "0.0.0.0", debug=true)

Displaying Errors with no_update

This example illustrates how you can show an error while keeping the previous input, using no_update to update only some of the callback outputs.

using Dash

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash(external_stylesheets=external_stylesheets)

app.layout = html_div() do
    html_p("Enter a composite number to see its prime factors"),
    dcc_input(id="num", type="number", debounce=true, min=1, step=1),
    html_p(id="err", style=Dict(:color => "red")),
    html_p(id="out")
end

callback!(
    app,
    Output("out", "children"),
    Output("err", "children"),
    Input("num", "value")
) do num
    if isnothing(num)
        # PreventUpdate prevents ALL outputs updating
        throw(PreventUpdate())
    end

    factors = prime_factors(num)
    if length(factors) == 1
        # dash.no_update prevents any single output updating
        # (note: it"s OK to use for a single-output callback too)
        return no_update(), "$(num) is prime!"
    end

    return "$num is $(join(string.(factors), " * "))", ""
end

function prime_factors(num::Int)
    n, i, out = num, 2, Int[]
    while i * i <= n
        if n % i == 0
            n = Int(n / i)
            push!(out, i)
        else
            i = i + (i == 2 ? 1 : 2)
        end
    end
    push!(out, n)
    return out
end

run_server(app, "0.0.0.0", debug=true)

Enter a composite number to see its prime factors

Determining which Input Has Fired with callback_context

In addition to event properties like n_clicks that change whenever an event happens (in this case a click), there is a function callback_context available only inside a callback. Using dash.callback_context, you can determine which component/property pairs triggered a callback.

Below is a summary of properties of dash.callback_context outlining the basics of when to use them. For more detail and examples see Determining Which Callback Input Changed.

Properties for callback_context

  • triggered_id: The id of the component that triggered the callback. Available in Dash 2.4 and later.
  • triggered_prop_ids: A dictionary of the component ids and props that triggered the callback. Useful when multiple inputs can trigger the callback at the same time, or multiple properties of the same component can trigger the callback. Available in Dash 2.4 and later.
  • args_grouping: A dictionary of the inputs used with flexible callback signatures. Available in Dash 2.4 and later.

    The keys are the variable names and the values are dictionaries containing:

    • "id": the component ID. If it’s a pattern matching ID, it will be a dict.
    • "id_str": for pattern matching IDs, it’s the stringified dict ID with no white spaces.
    • "property": the component property used in the callback.
    • "value": the value of the component property at the time the callback was fired.
    • "triggered": a boolean indicating whether this input triggered the callback.
  • triggered: list of all the Input props that changed and caused the callback to execute. It is empty when the callback is called on initial load, unless an Input prop got its value from another initial callback. Callbacks triggered by user actions typically have one item in triggered, unless the same action changes two props at once or the callback has several Input props that are all modified by another callback based on a single user action.

    More about empty triggered lists: For backward compatibility purposes, an empty triggered is not really empty. It's falsy so that you can use if triggered to detect the initial call, but it still has a placeholder element so that ctx.triggered[0]["prop_id"].split(".") yields a blank ID and prop ["", ""] instead of an error.

  • inputs and states: allow you to access the callback params by ID and prop instead of through the function args. These have the form of dictionaries { 'component_id.prop_name': value }

  • outputs_list, inputs_list, and states_list: lists of inputs, outputs, and state items arranged as you'll find them in the callback arguments and return value. This is mostly useful for pattern-matching callbacks.
  • response: The HTTP response object being constructed, useful for changing cookies.

Here's an example of how this can be done:

using Dash, JSON

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash(external_stylesheets=external_stylesheets)

app.layout = html_div() do
    html_button("Button 1", id="btn-1"),
    html_button("Button 2", id="btn-2"),
    html_button("Button 3", id="btn-3"),
    html_div(id="container")
end

callback!(
    app,
    Output("container", "children"),
    Input("btn-1", "n_clicks"),
    Input("btn-2", "n_clicks"),
    Input("btn-3", "n_clicks"),
) do btn1, btn2, btn3
    ctx = callback_context()

    if length(ctx.triggered) == 0
        button_id = "No clicks yet"
    else
        button_id = split(ctx.triggered[1].prop_id, ".")[1]
    end

    ctx_msg = Dict(
        :states => ctx.states,
        :triggered => ctx.triggered,
        :inputs => ctx.inputs,
    )
    io = IOBuffer()
    JSON.print(io, ctx_msg, 2)
    ctx_msg_string = String(take!(io))

    html_div() do
        html_table() do
            html_tr() do
                html_th("Button 1"),
                html_th("Button 2"),
                html_th("Button 3"),
                html_th("Most Recent Click")
            end,
            html_tr() do
                html_td(isnothing(btn1) ? 0 : btn1),
                html_td(isnothing(btn2) ? 0 : btn2),
                html_td(isnothing(btn3) ? 0 : btn3),
                html_td(button_id)
            end
        end,
        html_pre(ctx_msg_string)
    end
end

run_server(app, "0.0.0.0", debug=true)
Button 1Button 2Button 3Most Recent Click
000No clicks yet
{
  "states": {},
  "triggered": [],
  "inputs": {
    "btn-1.n_clicks": null,
    "btn-2.n_clicks": null,
    "btn-3.n_clicks": null
  }
}

Sign up for Dash Club → Two free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture. Join now.

When Are Callbacks Executed?

This section describes the circumstances under which the dash-renderer front-end client can make a request to the Dash back-end server (or the clientside callback code) to execute a callback function.

When a Dash App First Loads

All of the callbacks in a Dash app are executed with the initial value of their inputs when the app is first loaded. This is known as the "initial call" of the callback. To learn how to suppress this behavior, see the documentation for the prevent_initial_call attribute of Dash callbacks.

It is important to note that when a Dash app is initially loaded in a web browser by the dash-renderer front-end client, its entire callback chain is introspected recursively.

This allows the dash-renderer to predict the order in which callbacks will need to be executed, as callbacks are blocked when their inputs are outputs of other callbacks which have not yet fired. In order to unblock the execution of these callbacks, first callbacks whose inputs are immediately available must be executed. This process helps the dash-renderer to minimize the time and effort it uses, and avoid unnecessarily redrawing the page, by making sure it only requests that a callback is executed when all of the callback's inputs have reached their final values.

Examine the following Dash app:

using Dash

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash(external_stylesheets=external_stylesheets)

app.layout = html_div() do
    html_button("execute callback", id="button_1"),
    html_div(children="callback not executed", id="first_output_1"),
    html_div(children="callback not executed", id="second_output_1")
end


function change_text(n_clicks)
    "n_clicks is $n_clicks", "n_clicks is $n_clicks"
end

callback!(
    change_text,
    app,
    Output("first_output_1", "children"),
    Output("second_output_1", "children"),
    Input("button_1", "n_clicks"),
)

run_server(app, "0.0.0.0", debug=true)
n_clicks is None
n_clicks is None

Notice that when this app is finished being loaded by a web browser and ready for user interaction, the html_div components do not say "callback not executed" as declared in the app's layout, but rather "n_clicks is None" as the result of the change_text() callback being executed. This is because the "initial call" of the callback occurred with n_clicks having the value of None.

As a Direct Result of User Interaction

Most frequently, callbacks are executed as a direct result of user interaction, such as clicking a button or selecting an item in a dropdown menu. When such interactions occur, Dash components communicate their new values to the dash-renderer front-end client, which then requests that the Dash server execute any callback function that has the newly changed value as input.

If a Dash app has multiple callbacks, the dash-renderer requests callbacks to be executed based on whether or not they can be immediately executed with the newly changed inputs. If several inputs change simultaneously, then requests are made to execute them all.

Whether or not these requests are executed in a synchronous or asynchronous manner depends on the specific setup of the Dash back-end server. If it is running in a multi-threaded environment, then all of the callbacks can be executed simultaneously, and they will return values based on their speed of execution. In a single-threaded environment however, callbacks will be executed one at a time in the order they are received by the server.

In the example application above, clicking the button results in the callback being executed.

As an Indirect Result of User Interaction

When a user interacts with a component, the resulting callback might have outputs that are themselves the input of other callbacks. The dash-renderer will block the execution of such a callback until the callback whose output is its input has been executed.

Take the following Dash app:

using Dash, Dates

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash(external_stylesheets=external_stylesheets)

app.layout = html_div() do
    html_button("execute fast callback", id="button_3"),
    html_button("execute slow callback", id="button_4"),
    html_div(children="callback not executed", id="first_output_3"),
    html_div(children="callback not executed", id="second_output_3"),
    html_div(children="callback not executed", id="third_output_3")
end

callback!(
    app,
    Output("first_output_3", "children"),
    Input("button_3", "n_clicks"),
) do n
    "in the fast callback it is $(Dates.format(now(), "HH:MM:SS"))"
end

callback!(
    app,
    Output("second_output_3", "children"),
    Input("button_4", "n_clicks"),
) do n
    sleep(5)
    "in the slow callback it is $(Dates.format(now(), "HH:MM:SS"))"
end

callback!(
    app,
    Output("third_output_3", "children"),
    Input("first_output_3", "children"),
    Input("second_output_3", "children"),
) do n, m
    "in the third callback it is $(Dates.format(now(), "HH:MM:SS"))"
end

run_server(app, "0.0.0.0", debug=true)
in the fast callback it is 20:08:02
callback not executed
callback not executed

The above Dash app demonstrates how callbacks chain together. Notice that if you first click "execute slow callback" and then click "execute fast callback", the third callback is not executed until after the slow callback finishes executing. This is because the third callback has the second callback's output as its input, which lets the dash-renderer know that it should delay its execution until after the second callback finishes.

When Dash Components Are Added to the Layout

It is possible for a callback to insert new Dash components into a Dash app's layout. If these new components are themselves the inputs to other callback functions, then their appearance in the Dash app's layout will trigger those callback functions to be executed.

In this circumstance, it is possible that multiple requests are made to execute the same callback function. This would occur if the callback in question has already been requested and its output returned before the new components which are also its inputs are added to the layout.

Prevent Callback Execution Upon Initial Component Render

You can use the prevent_initial_call attribute to prevent callbacks from firing when their inputs initially appear in the layout of your Dash application.

This attribute applies when the layout of your Dash app is initially loaded, and also when new components are introduced into the layout when a callback has been triggered.

using Dash, Dates

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash(external_stylesheets=external_stylesheets)

app.layout = html_div() do
    html_button("execute callbacks", id="button_2"),
    html_div(children="callback not executed", id="first_output_2"),
    html_div(children="callback not executed", id="second_output_2"),
    html_div(children="callback not executed", id="third_output_2"),
    html_div(children="callback not executed", id="fourth_output_2")
end

callback!(
    app,
    Output("first_output_2", "children"),
    Output("second_output_2", "children"),
    Input("button_2", "n_clicks"),
    prevent_initial_call=true,
) do n
    s = "in the first callback it is $(Dates.format(now(), "HH:MM:SS"))"
    return s, s
end

callback!(
    app,
    Output("third_output_2", "children"),
    Input("second_output_2", "children"),
    prevent_initial_call=true,
) do n
    sleep(2)
    "in the second callback it is $(Dates.format(now(), "HH:MM:SS"))"
end

callback!(
    app,
    Output("fourth_output_2", "children"),
    Input("first_output_2", "children"),
    Input("third_output_2", "children"),
    prevent_initial_call=true,
) do n, m
    "in the third callback it is $(Dates.format(now(), "HH:MM:SS"))"
end

run_server(app, "0.0.0.0", debug=true)
callback not executed
callback not executed
callback not executed
callback not executed

However, the above behavior only applies if both the callback output and input are present in the app layout upon initial load of the application.

It is important to note that prevent_initial_call will not prevent a callback from firing in the case where the callback's input is inserted into the layout as the result of another callback after the app initially loads unless the output is inserted alongside that input!

In other words, if the output of the callback is already present in the app layout before its input is inserted into the layout, prevent_initial_call will not prevent its execution when the input is first inserted into the layout.

Consider the following example:

using Dash, Dates

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash(external_stylesheets=external_stylesheets, suppress_callback_exceptions=true)

app.layout = html_div() do
    dcc_location(id="url"),
    html_div(id="layout-div"),
    html_div(id="content")
end

callback!(
    app,
    Output("content", "children"),
    Input("url", "pathname")
) do pathanme
    html_div([
        dcc_input(id="input", value="hello world"),
        html_div(id="output")
    ])
end

callback!(
    app,
    Output("output", "children"),
    Input("input", "value"),
    prevent_initial_call=true
) do value
    println(">>> update_output")
    value
end

callback!(
    app,
    Output("layout-div", "children"),
    Input("input", "value"),
    prevent_initial_call=true
)  do value
    println(">>> update_layout_div")
    value
end

In this case, prevent_initial_call will prevent the update_output() callback from firing when its input is first inserted into the app layout as a result of the display_page() callback. This is because both the input and output of the callback are already contained within the app layout when the callback executes.

However, because the app layout contains only the output of the callback, and not its input, prevent_initial_call will not prevent the update_layout_div() callback from firing. Since suppress_callback_exceptions=True is specified here, Dash has to assume that the input is present in the app layout when the app is initialized. From the perspective of the output element in this example, the new input component is handled as if an existing input had been provided a new value, rather than treating it as initially rendered.

Circular Callbacks

As of dash v1.19.0, you can create circular updates within the same callback.

Circular callback chains that involve multiple callbacks are not supported.

Circular callbacks can be used to keep multiple inputs synchronized to each other.

Synchronizing a Slider with a Text Input Example

# TODO: as of 2021-08-11 this example does not work because circular updates
#       are not supported, even within the same callback
using Dash, Dates

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash(external_stylesheets=external_stylesheets, suppress_callback_exceptions=true)

app.layout = html_div() do
    dcc_slider(
        id="slider-circular",
        min=0,
        max=20,
        marks=Dict(i => "$i" for i in 0:20),
        value=3,
    ),
    dcc_input(id="input-circular", type="number", min=0, max=20, value=3)
end

callback!(
    app,
    Output("input-circular", "value"),
    Output("slider-circular", "value"),
    Input("input-circular", "value"),
    Input("slider-circular", "value"),
) do input_value, slider_value
    ctx = callback_context()
    trigger_id = split(ctx.triggered[1].prop_id)[1]
    value = trigger_id == "input-circular" ? input_value : slider_value
    return value, value
end

run_server(app, "0.0.0.0", 8432, debug=true)
01234567891011121314151617181920

Displaying Two Inputs with Different Units Example

This example has not been ported to Julia yet - showing the Python version instead.

Visit the old docs site for Julia at: https://community.plotly.com/c/dash/julia/20

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

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    html.Div('Convert Temperature'),
    'Celsius',
    dcc.Input(
        id="celsius",
        value=0.0,
        type="number"
    ),
    ' = Fahrenheit',
    dcc.Input(
        id="fahrenheit",
        value=32.0,
        type="number",
    ),
])

@callback(
    Output("celsius", "value"),
    Output("fahrenheit", "value"),
    Input("celsius", "value"),
    Input("fahrenheit", "value"),
)
def sync_input(celsius, fahrenheit):
    input_id = ctx.triggered[0]["prop_id"].split(".")[0]
    if input_id == "celsius":
        fahrenheit= None if celsius is None else (float(celsius) * 9/5) + 32
    else:
        celsius = None if fahrenheit is None else (float(fahrenheit) - 32) * 5/9
    return celsius, fahrenheit

if __name__ == "__main__":
    app.run(debug=True)
Convert Temperature
Celsius = Fahrenheit

Synchronizing Two Checklists

This example has not been ported to Julia yet - showing the Python version instead.

Visit the old docs site for Julia at: https://community.plotly.com/c/dash/julia/20

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

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = Dash(__name__, external_stylesheets=external_stylesheets)

options = ["New York City", "Montréal", "San Francisco"]

app.layout = html.Div(
    [
        dcc.Checklist(["All"], [], id="all-checklist", inline=True),
        dcc.Checklist(options, [], id="city-checklist", inline=True),
    ]
)
@callback(
    Output("city-checklist", "value"),
    Output("all-checklist", "value"),
    Input("city-checklist", "value"),
    Input("all-checklist", "value"),
)
def sync_checklists(cities_selected, all_selected):
    ctx = callback_context
    input_id = ctx.triggered[0]["prop_id"].split(".")[0]
    if input_id == "city-checklist":
        all_selected = ["All"] if set(cities_selected) == set(options) else []
    else:
        cities_selected = options if all_selected else []
    return cities_selected, all_selected

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