How to create custom actions
Actions control how your app responds to user input such as clicking a button or a point on a graph. If an action is not available in Vizro's built-in actions then you can create a custom action. In this guide we show how to do this.
We also have an in-depth tutorial on creating an action and an explanation of how Vizro actions work.
Note
Do you have an idea for a built-in action? Submit a feature request!
General principles
Many Vizro models have an actions argument that can contain one or more actions. Each action is a Python function that is triggered by a user interaction. The function can optionally have any number of inputs and outputs that refer to a Vizro model id.
To define your own action:
-
write a Python function and decorate it with
@capture("action"): -
attach it to the
actionsargument of a Vizro model usingAction:- call it using the
functionargument - if your action has one or more inputs then specify them as function arguments
- if your action has one or more outputs then specify them as
outputs
import vizro.models as vm actions = vm.Action( function=action_function(input_1="input_id_1", input_2="input_id_2"), # (1)! outputs="output_id_1", # (2)! )- When the dashboard is running, the action's
input_1will be set to the runtime value of the Vizro model withid="input_id_1"and similarly forinput_2. - When the dashboard is running, the action's output "My string value..." will set the value of the Vizro model with
id="output_id_1".
- call it using the
You can also execute multiple actions with a single trigger.
Warning
You should never assume that the values of inputs in your action function are restricted to those that show on the user's screen. A malicious user can execute your action functions with arbitrary inputs. In the tutorial, we discuss in more detail how to write secure actions.
Trigger an action with a button
Here is an example action that gives the current time when a button is clicked.
from datetime import datetime
from vizro.models.types import capture
@capture("action")
def current_time_text(): # (1)!
time = datetime.now()
return f"The time is {time}" # (2)!
- The function has no input arguments.
- The function returns a single value.
To attach the action to a button model, we use it inside the actions argument as follows:
- Call the action function with
function=current_time_text()(remember the()). - The returned value "The time is ..." will update the component
id="time_text"(not yet defined).
Here is the full example code that includes the output component vm.Time(id="time_text").
Trigger an action with a button
from datetime import datetime
import vizro.models as vm
from vizro import Vizro
from vizro.models.types import capture
@capture("action")
def current_time_text():
time = datetime.now()
return f"The time is {time}"
page = vm.Page(
title="Action triggered by button",
layout=vm.Flex(),
components=[
vm.Button(
actions=vm.Action(
function=current_time_text(),
outputs="time_text",
)
),
vm.Text(id="time_text", text="Click the button"),
],
)
dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()
Run and edit this code in Py.Cafe
# Still requires a .py to define a CapturedCallables custom action and parse YAML configuration
# More explanation in the docs on `Dashboard` and extensions.
pages:
- components:
- type: button
actions:
- type: action
function:
_target_: __main__.current_time_text
outputs: time_text
- type: text
id: time_text
text: Click the button
layout:
type: flex
title: Action triggered by button

Before clicking the button, the text shows "Click the button". When you click the button, the current_time_text action is triggered. This finds the current time and returns a string "The time is ...". The resulting value is sent back to the user's screen and updates the text of the model vm.Text(id="time_text").
Tip
If you have many buttons that trigger actions then you might like to give them icons. You can even have icon-only buttons with no text.
Trigger an action with a graph
This is already possible, and documentation is coming soon!
Trigger with a runtime input
This extends the above example of an action triggered by a button to include an input. Here is the action function:
from datetime import datetime
from vizro.models.types import capture
@capture("action")
def current_time_text(use_24_hour_clock): # (1)!
time_format = "%H:%M:%S" if use_24_hour_clock else "%I:%M:%S %p"
time = datetime.now().strftime(time_format)
return f"The time is {time}" # (2)!
- The function has one argument, which will receive a boolean value
TrueorFalseto determine the time format used. - The function returns a single value.
To attach the action to a button model, we use it inside the actions argument as follows:
vm.Button(
actions=vm.Action(
function=current_time_text(use_24_hour_clock="clock_switch"), # (1)!
outputs="time_text", # (2)!
),
)
- The argument
use_24_hour_clockcorresponds to the value of the component withid="clock_switch"(not yet defined). Here we used a keyword argumentuse_24_hour_clock="clock_switch"but, as with normal Python function call, we could instead use a positional argument withcurrent_time_text("clock_switch"). - The returned value "The time is ..." will update the component
id="time_text"(not yet defined).
Here is the full example code that includes the input component vm.Switch(id="clock_switch") and the output component vm.Time(id="time_text").
Use runtime inputs
from datetime import datetime
import vizro.models as vm
from vizro import Vizro
from vizro.models.types import capture
@capture("action")
def current_time_text(use_24_hour_clock):
time_format = "%H:%M:%S" if use_24_hour_clock else "%I:%M:%S %p"
time = datetime.now().strftime(time_format)
return f"The time is {time}"
vm.Page.add_type("components", vm.Switch) # (1)!
page = vm.Page(
title="Action triggered by button",
layout=vm.Flex(),
components=[
vm.Switch(id="clock_switch", title="24-hour clock", value=True),
vm.Button(
actions=vm.Action(
function=current_time_text(use_24_hour_clock="clock_switch"),
outputs="time_text",
),
),
vm.Text(id="time_text", text="Click the button"),
],
)
dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()
Run and edit this code in Py.Cafe
- Currently
Switchis designed to be used as a control selectors. In future, Vizro will have a dedicatedFormmodel for the creation of forms. For now, we add them directly ascomponentsinside aContainer. For this to be a valid configuration we must first doadd_typeas for a custom component.

Before clicking the button, the text shows "Click the button". When you click the button, the current_time_text action is triggered. This finds the current time and returns a string "The time is ..." with a time format that depends on the switch's setting. The resulting value is sent back to the user's screen and updates the text of the model vm.Text(id="time_text").
Multiple inputs and outputs
An action can have any number of inputs and outputs (including zero). Here is an action with two inputs and two outputs:
from vizro.models.types import capture
@capture("action")
def action_function(input_1, input_2):
...
return "My string value 2", "My string value 2"
This would be attached to an actions argument as follows:
import vizro.models as vm
actions = vm.Action(
function=action_function(input_1="input_id_1", input_2="input_id_2"), # (1)!
outputs=["output_id_1", "output_id_2"],
)
- As with an ordinary Python function call, this could also be written using positional arguments as
action_function("input_id_1", "input_id_2").
The returned values of an action function with multiple outputs are matched to the outputs in order. For actions with many return values, it can be a good idea to instead return a dictionary where returned values are labeled by string keys. In this case, outputs should also be a dictionary with matching keys, and the order of entries does not matter:
@capture("action")
def action_function(input_1, input_2):
...
return {"key 1": "My string value 2", "key 2": "My string value 2"}
actions = vm.Action(
function=action_function(input_1="input_id_1", input_2="input_id_2"),
outputs={"key 1": "output_id_1", "key 2": "output_id_2"}, # (1)!
)
- Specifying outputs in the "wrong" order as
outputs={"key 2": "output_id_2", "key 1": "output_id_1"}would work exactly the same way.
A full real world example of using multiple inputs and outputs is given in the tutorial.
Multiple actions
When you specify multiple actions as actions=[action_1, action_2, ...] then Vizro chains these actions in order, so that action_2 executes only when action_1 has completed. You can freely mix built-in actions and custom actions in an actions chain. For more details on how actions chains execute, see our tutorial on custom actions.
Here is an example actions chain that uses a custom action_function action and the built-in export_data action:
import vizro.actions as va
import vizro.models as vm
actions = [
va.export_data(),
vm.Action(
function=action_function("input_id_1", "input_id_2"),
outputs="output_id",
),
]
Address specific parts of a model
For most actions that you write, you should only need to specify <model_id> for the outputs or as input arguments to the action function. However, some models have multiple arguments that you may want to use in an action. This is possible with the syntax <model_id>.<argument_name>. For more advanced use cases you can even address the underlying Dash component and property.
Model arguments as input and output
The syntax for using a particular model argument as an action input or output is <model_id>.<argument_name>.
For example, let's alter the above example of a switch that toggles between formatting time with the 12- and 24-hour clock. Switch has an argument title that adds a label to the switch. We can update this in an action by including clock_switch.title in the action's outputs.
Use model argument as output
from datetime import datetime
import vizro.models as vm
from vizro import Vizro
from vizro.models.types import capture
@capture("action")
def current_time_text(use_24_hour_clock):
time_format = "%H:%M:%S" if use_24_hour_clock else "%I:%M:%S %p"
switch_title = "24-hour clock" if use_24_hour_clock else "12-hour clock"
time = datetime.now().strftime(time_format)
return f"The time is {time}", switch_title
vm.Page.add_type("components", vm.Switch) # (1)!
page = vm.Page(
title="Action triggered by switch",
layout=vm.Flex(),
components=[
vm.Switch(
id="clock_switch",
title="24-hour clock",
value=True,
actions=vm.Action( # (2)!
function=current_time_text(use_24_hour_clock="clock_switch"),
outputs=["time_text", "clock_switch.title"], # (3)!
),
),
vm.Text(id="time_text", text="Toggle the switch"),
],
)
dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()
Run and edit this code in Py.Cafe
- Currently
Switchis designed to be used as a control selectors. In future, Vizro will have a dedicatedFormmodel for the creation of forms. For now, we add them directly ascomponentsinside aContainer. For this to be a valid configuration we must first doadd_typeas for a custom component. - In the previous example, the action was triggered when a button is clicked; now we change the action to be triggered when the switch itself is clicked.
- This action now has two
outputs. We refer to"clock_switch.title"to update the title of the switch.

Dash properties as input and output
Sometimes you might like to use as input or output a component that is on the screen but cannot be addressed explicitly with <model_id>.<argument_name>. Vizro actions in fact accept as input and output any Dash component in the format <component_id>.<property>.
For example, let's alter the above example of a switch that toggles between formatting time with the 12- and 24-hour clock. We want to disable the switch when the button is clicked so that it can no longer be toggled. Switch does not contain an argument to disable the switch, but the underlying Dash component dbc.Switch does. We can address this by using "clock_switch.disabled" in our outputs.
Use Dash property as input
from datetime import datetime
import vizro.models as vm
from vizro import Vizro
from vizro.models.types import capture
@capture("action")
def current_time_text(use_24_hour_clock):
time_format = "%H:%M:%S" if use_24_hour_clock else "%I:%M:%S %p"
time = datetime.now().strftime(time_format)
return f"The time is {time}", True # (1)!
vm.Page.add_type("components", vm.Switch) # (2)!
page = vm.Page(
title="Action triggered by button",
layout=vm.Flex(),
components=[
vm.Switch(id="clock_switch", title="24-hour clock", value=True),
vm.Button(
actions=vm.Action(
function=current_time_text(use_24_hour_clock="clock_switch"),
outputs=["time_text", "clock_switch.disabled"], # (3)!
),
),
vm.Text(id="time_text", text="Click the button"),
],
)
dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()
Run and edit this code in Py.Cafe
- We disable the switch by returning
Trueto itsdisabledproperty. After this action runs, the switch can no longer the clicked. To reset it, you must refresh the page. - Currently
Switchis designed to be used as a control selectors. In future, Vizro will have a dedicatedFormmodel for the creation of forms. For now, we add them directly ascomponentsinside aContainer. For this to be a valid configuration we must first doadd_typeas for a custom component. - This action now has two
outputs. We refer to"clock_switch.disabled"to update thedisabledproperty of the component withid="clock_switch".
