Skip to content

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:

  1. write a Python function and decorate it with @capture("action"):

    from vizro.models.types import capture
    
    
    @capture("action")
    def action_function(input_1, input_2):
        ...
        return "My string value, potentially dependent on input_1 and input_2"
    
  2. attach it to the actions argument of a Vizro model using Action:

    1. call it using the function argument
    2. if your action has one or more inputs then specify them as function arguments
    3. 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)!
    )
    
    1. When the dashboard is running, the action's input_1 will be set to the runtime value of the Vizro model with id="input_id_1" and similarly for input_2.
    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".

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)!
  1. The function has no input arguments.
  2. 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(),  # (1)!
        outputs="time_text",  # (2)!
    ),
)
  1. Call the action function with function=current_time_text() (remember the ()).
  2. 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)!
  1. The function has one argument, which will receive a boolean value True or False to determine the time format used.
  2. 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)!
    ),
)
  1. The argument use_24_hour_clock corresponds to the value of the component with id="clock_switch" (not yet defined). Here we used a keyword argument use_24_hour_clock="clock_switch" but, as with normal Python function call, we could instead use a positional argument with current_time_text("clock_switch").
  2. 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

  1. Currently Switch is designed to be used as a control selectors. In future, Vizro will have a dedicated Form model for the creation of forms. For now, we add them directly as components inside a Container. For this to be a valid configuration we must first do add_type as for a custom component.
# Custom components and added component types (with `add_type`) are currently only possible via Python configuration

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"],
)
  1. 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)!
)
  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

  1. Currently Switch is designed to be used as a control selectors. In future, Vizro will have a dedicated Form model for the creation of forms. For now, we add them directly as components inside a Container. For this to be a valid configuration we must first do add_type as for a custom component.
  2. 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.
  3. This action now has two outputs. We refer to "clock_switch.title" to update the title of the switch.
# Custom components and added component types (with `add_type`) are currently only possible via Python configuration

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

  1. We disable the switch by returning True to its disabled property. After this action runs, the switch can no longer the clicked. To reset it, you must refresh the page.
  2. Currently Switch is designed to be used as a control selectors. In future, Vizro will have a dedicated Form model for the creation of forms. For now, we add them directly as components inside a Container. For this to be a valid configuration we must first do add_type as for a custom component.
  3. This action now has two outputs. We refer to "clock_switch.disabled" to update the disabled property of the component with id="clock_switch".
# Custom components and added component types (with `add_type`) are currently only possible via Python configuration