Advanced Callbacks _ Dash for Python Documentation _ Plotly
Advanced Callbacks _ Dash for Python Documentation _ Plotly
Python
Advanced Callbacks
To get the most out of this page, make sure you've read about Basic Callbacks in
the Dash Fundamentals.
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP
app.layout = html.Div([
html.Button('Click here to see the content', id='show-secre
html.Div(id='body-div')
])
@callback(
Output('body-div', 'children'),
Input('show-secret', 'n_clicks')
)
def update_output(n_clicks):
if n_clicks is None:
raise PreventUpdate
else:
return "Elephants are the only animal that can't jump"
if __name__ == '__main__':
app.run(debug=True)
from dash import Dash, dcc, html, Input, Output, callback, no_
from dash.exceptions import PreventUpdate
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwg
app.layout = html.Div([
html.P('Enter a composite number to see its prime factors'
dcc.Input(id='num', type='number', debounce=True, min=2, s
html.P(id='err', style={'color': 'red'}),
html.P(id='out')
])
@callback(
Output('out', 'children'),
Output('err', 'children'),
Input('num', 'value')
)
def show_factors(num):
if num is None:
# PreventUpdate prevents ALL outputs updating
raise PreventUpdate
factors = prime_factors(num)
if len(factors) == 1:
# dash.no_update prevents any single output updating
# (note: it's OK to use for a single-output callback t
return no_update, '{} is prime!'.format(num)
def prime_factors(num):
n, i, out = num, 2, []
The second element is the value that the property should be set to
while the callback is running.
The third element is the value the property should be set to when the
callback completes.
from dash import Dash, dcc, html, Input, Output, State, callbac
import time
app = Dash()
app.layout = html.Div([
html.Div(dcc.Input(id='input-on-submit-text', type='text'))
html.Button('Submit', id='submit-button', n_clicks=0),
html.Div(id='container-output-text',
children='Enter a value and press submit')
])
@callback(
Output('container-output-text', 'children'),
Input('submit-button', 'n_clicks'),
State('input-on-submit-text', 'value'),
prevent_initial_call=True,
running=[(Output("submit-button", "disabled"), True, False)
)
def update_output(n_clicks, value):
time.sleep(5)
return 'The input value was "{}" and the button has been cl
value,
n_clicks
)
if __name__ == '__main__':
app.run(debug=True)
SUBMIT
There is a known issue where using running with a multi-pages app doesn't work
as expected when a user changes page when the callback is running.
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.
import json
from dash import Dash, html, Input, Output, callback, ctx
app = Dash()
app.layout = html.Div([
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')
])
@callback(Output('container', 'children'),
Input('btn-1', 'n_clicks'),
Input('btn-2', 'n_clicks'),
Input('btn-3', 'n_clicks'))
def display(btn1, btn2, btn3):
if not ctx.triggered_id:
button_id = 'No clicks yet'
else:
button_id = ctx.triggered_id
ctx_msg = json.dumps({
'states': ctx.states,
'triggered': ctx.triggered,
'inputs': ctx.inputs
}, indent=2)
return html.Div([
html.Table([
html.Tr([html.Th('Button 1'),
html.Th('Button 2'),
html.Th('Button 3'),
0 0 0 No clicks yet
{
"states": {},
"triggered": [],
"inputs": {
"btn-1.n_clicks": null,
"btn-2.n_clicks": null,
"btn-3.n_clicks": null
}
}
Improving Performance with Memoization
Memoization allows you to bypass long computations by storing the results
of function calls.
import time
import functools32
@functools32.lru_cache(maxsize=32)
def slow_function(input):
time.sleep(10)
return f'Input was {input}'
The Performance section of the Dash docs delves a little deeper into
leveraging multiple processes and threads in conjunction with memoization
to further improve performance.
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.
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.
app = Dash()
app.layout = html.Div(
[
html.Button("execute callback", id="button_1"),
html.Div(children="callback not executed", id="first_ou
html.Div(children="callback not executed", id="second_o
]
)
@callback(
Output("first_output_1", "children"),
Output("second_output_1", "children"),
Input("button_1", "n_clicks")
)
def change_text(n_clicks):
return ["n_clicks is " + str(n_clicks), "n_clicks is " + st
if __name__ == '__main__':
app.run(debug=True)
EXECUTE CALLBACK
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 .
In the example application above, clicking the button results in the callback
being executed.
app = Dash()
app.layout = html.Div(
[
html.Button("execute fast callback", id="button_3"),
html.Button("execute slow callback", id="button_4"),
html.Div(children="callback not executed", id="first_o
html.Div(children="callback not executed", id="second_
html.Div(children="callback not executed", id="third_o
]
)
@callback(
Output("first_output_3", "children"),
Input("button_3", "n_clicks"))
def first_callback(n):
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
return "in the fast callback it is " + current_time
@callback(
Output("second_output_3", "children"), Input("button_4", "
def second_callback(n):
time.sleep(5)
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
return "in the slow callback it is " + current_time
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.
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.
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.
app.layout = html.Div(
[
html.Button("execute callbacks", id="button_2"),
html.Div(children="callback not executed", id="first_o
html.Div(children="callback not executed", id="second_
html.Div(children="callback not executed", id="third_o
html.Div(children="callback not executed", id="fourth_
]
)
@callback(
Output("first_output_2", "children"),
Output("second_output_2", "children"),
Input("button_2", "n_clicks"), prevent_initial_call=True)
def first_callback(n):
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
return ["in the first callback it is " + current_time, "in
@callback(
Output("third_output_2", "children"), Input("second_output
def second_callback(n):
time.sleep(2)
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
EXECUTE CALLBACKS
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.
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.
All previous examples have inputs that trigger callbacks as well as outputs
that are updated. Callbacks also support having no outputs. This can be
useful for cases where you want some code to run when an action triggers a
callback, but you don't want to update any component property. For
example, you may want to fetch some data and save it, send emails, or
interact with other external services outside of the Dash ecosystem.
import base64
from dash import Dash, html, Input, Output, dcc, callback, Sta
app = Dash(__name__)
app.layout = html.Div(
[
html.H1("No Output Example"),
dcc.Upload(
id='upload-data-to-server',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
style={
'width': '100%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center',
'margin': '10px'
},
),
]
)
@callback(
Input("upload-data-to-server", "contents"),
State('upload-data-to-server', 'filename'),
Here, we have a callback that uses set_props to set the modal's is_open
property to False when the callback is triggered by a user selecting the
modal_close button. Otherwise, it's set to True and the modal content is
displayed. We have no outputs on this callback, but set_props can also be
combined with outputs.
This example is based on the Popup On Row Selection Example on the Dash
AG Grid page, which instead uses outputs and dash.no_update .
On callbacks that don't run in the background, like in the following example,
updates made using set_props and via outputs all happen at the same time,
when the callback finishes running. On background callbacks, set_props
updates take place immediately. See the Background Callbacks page for an
example.
rowData = [
{"make": "Toyota", "model": "Celica", "price": 35000},
{"make": "Ford", "model": "Mondeo", "price": 32000},
{"make": "Porsche", "model": "Boxster", "price": 72000},
]
app.layout = html.Div(
[
dag.AgGrid(
id="setprops-row-selection-popup",
rowData=rowData,
columnDefs=[{"field": i} for i in ["make", "model"
columnSize="sizeToFit",
dashGridOptions={"rowSelection": "single", "animat
),
dbc.Modal(
[
dbc.ModalHeader("More information about select
dbc.ModalBody(id="setprops-row-selection-modal
dbc.ModalFooter(dbc.Button("Close", id="setpro
],
id="setprops-row-selection-modal",
),
]
)
@callback(
Input("setprops-row-selection-popup", "selectedRows"),
Limitations
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.
from dash import Dash, html, dcc, Input, Output, callback, ctx
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP
app.layout = html.Div(
[
dcc.Slider(
id="slider-circular", min=0, max=20,
marks={i: str(i) for i in range(21)},
value=3
),
dcc.Input(
id="input-circular", type="number", min=0, max=20,
),
]
)
@callback(
Output("input-circular", "value"),
Output("slider-circular", "value"),
Input("input-circular", "value"),
Input("slider-circular", "value"),
)
def callback(input_value, slider_value):
trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
value = input_value if trigger_id == "input-circular" else
return value, value
if __name__ == '__main__':
app.run(debug=True)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwg
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(celsiu
else:
celsius = None if fahrenheit is None else (float(fahre
Convert Temperature
Celsius 0 = Fahrenheit
32
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP
app.layout = html.Div(
[
dcc.Checklist(["All"], [], id="all-checklist", inline=T
dcc.Checklist(options, [], id="city-checklist", inline=
]
)
@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(o
else:
cities_selected = options if all_selected else []
return cities_selected, all_selected
if __name__ == "__main__":
app.run(debug=True)
All
New York City Montréal San Francisco
SUBSCRIBE
Copyright © 2025 Plotly. All rights reserved. Terms of Service Privacy Policy