Best Practices for Reactivity in Screen Flows

Ensure your Lightning web components integrate well within the Flow runtime engine and work as expected for screen flow reactivity.

These sections describe state management with reactivity in screen flows, including:

  • How LWC state management differs from Aura
  • How state management works in the new LWC Flow Runtime
  • State management and event best practices to follow when building LWCs for your screen flows

State management for Aura components and LWCs follow different paradigms. In Aura components, <aura:attributes /> are viewed and modified by the component to which they belong, including external components. LWCs are designed to have a clear separation between internal and external states. Variables declared with @api are intended to be modified only by the LWC’s parents. Variables without the @api annotation and variables with @track are considered internal states and can be changed within the LWC that defines them.

The Flow runtime adheres to LWC state management design principles. It works best when the LWC components that it renders do not modify their own @api attributes. Instead, components request a change by firing FlowAttributeChangeEvent, which is essential for several reasons.

  • Components that allow their parents to control their @api properties are more easily reused because they allow the application to control the state rather than the component’s own internal business logic.
  • By firing attribute change events, the Flow runtime maintains an accurate picture of the component state. The Flow runtime can control inter-component interactions such as conditional field visibility, which is part of reactivity.

Using LWC linter rules is an excellent way to ensure that your component does not modify @api properties. Set up a linter that uses LWC’s linter rules in your development environment, specifically ensuring that the setting is enforced.

To maintain consistent behavior between Flow Runtime versions, when a user clicks the Next or Finish button, the Flow Runtime collects values by extracting each custom LWC component’s current @api attribute state before moving to the next screen or finishing the flow. This mechanism ensures that components that do not adhere to the prescribed design pattern continue to work in the latest Flow Runtime version. It is strongly recommended to avoid relying on this behavior.

Here is an example of a simple text input component that demonstrates firing FlowAttributeChangeEvent instead of updating the LWC’s @api property:

The following example isn’t recommended.

The FlowAttributeChangeEvent event provides LWCs the ability to inform the runtime of their current state. It is part of Flow Runtime’s state management mechanism. These events are meant to be fired whenever user interaction results in a modification that requires an update to an @api property.

Import FlowAttributeChangeEvent from the lightning/flowSupport module.

Next, you can create an instance of the event.

FlowAttributeChangeEvent includes two arguments.

  • apiParameterName—The name of the API property to modify.
  • updatedValue—The updated value for the Flow attribute.

Pass a string to apiParameterName that matches the API property as it appears in the JavaScript and .js-meta.xml files.

For example, notice how exampleApiParameterName is consistent in all 3 places:

  • Declare the attribute in the LWC’s .js-meta.xml file:

  • Declare the API property in the LWC’s .js file:

  • Construct the FlowAttributeChangeEvent instance:

Pass the updatedValue to match the declared datatype in the .js-meta.xml file.

  • Declare the property as a Record:

  • Declare the property as a string:

  • Declare the property as a number:

  • Declare the property as a boolean:

After constructing a FlowAttributeChangeEvent instance, you can fire the event using the this.dispatchEvent(eventToFire) method.

Follow these FlowAttributeChangeEvent examples and steer clear of problematic usages to reduce unexpected behaviors after a flow version upgrade. These best practices enable you to maintain your components more easily.

  • Fire events in event handlers or in methods invoked within event handlers.
  • Limit an event’s value parameter to the following datatypes: String, number, boolean, JSON (for record types).
  • Ensure that the Flow datatype of the event’s value parameter matches the datatype of the LWC’s @api property.
  • React to changes to @api properties by using a get/set pattern where appropriate.

This example demonstrates using a getter and setter to count the updates with an @api property vs. incrementing a counter before firing an attribute change event. Both patterns are valid, but they achieve slightly different results.

In this case, this.changeCounter is incremented whenever the user enters a change in the text input and whenever the Flow Runtime determines that the textValue property has changed. This approach accounts for component initialization and cross-component interactions via reactivity. Use an intermediate variable to render updates from the set method. In this example, textValueToRender fills this role.

The following code example increments a counter before firing an attribute change event. This scenario doesn’t use the get/set pattern.

In this case, this.changeCounter is incremented whenever the user inputs a change in the text input. The counter does not update when reactivity changes the value of this field.

Use annotationless or @track properties to maintain a modifiable local component state.

The following example shows a color picker that presents the user with an input text box and some swatches. The color picker component returns color to the flow, so only color needs the @api annotation. The selectedSwatchId and inputValue members are internal only and do not need any decorator.

Use a get method to combine multiple @api properties together to construct a derived variable for the view.

Apply the same logic in the set method of every @api property that contributes to a derived member variable needed for internal component state management, as in more complex scenarios than the previous example. This logic ensures that a change made to any contributing attribute causes the value to render correctly regardless of the order of the set attributes.

Don’t modify @api properties. Fire FlowAttributeChangeEvents instead.

Don’t modify FlowAttributeChangeEvent parameters after construction. By default, FlowAttributeChangeEvent instances are composed and bubble.

Avoid firing FlowAttributeChangeEvents and FlowNavigationXxx events at the same time as it can lead to race conditions. There's never a guarantee that your Lightning web component has had time to render the updated values by the time the navigation process is started.

In a flow component, a derived attribute is an attribute whose value is determined by other attributes of the same component. The value may be tied to one or more other attributes. For example, the firstSelectedRow parameter of the Data Table component is a derived attribute because its value is determined by the value of the selectedRows attribute. Specifically, the value of the firstSelectedRow parameter is the value of the first element of the selectedRows attribute.

When you create a flow screen component that includes a derived attribute, follow the best practices outlined here to ensure that the component functions properly within the reactivity framework. With these best practices, you can avoid issues related to:

A reactivity chain occurs when a change to an attribute of one component results in a cascade of changes to attributes of other components. For example, a user selects a row in a Data Table component, which changes the value of the firstSelectedRow attribute of the component, which changes the firstName attribute of a Name component. The change to the Name component changes the value attribute of a Text component.

When an admin configures a screen, you choose whether the flow preserves user-specified values when the user leaves and then returns to the screen using the Previous or Next button.

To ensure that derived attributes behave as expected in flow screens with reactive components, follow these guidelines:

  1. Do fire a FlowAttributeChangeEvent event for the derived attribute in the set method of the attribute that drives the change. Also fire a FlowAttributeChangeEvent event for the derived attribute in the connectedCallback method of the component, for example:

  2. Don't omit the FlowAttributeChangeEvent event in the connectedCallback method. You must include the FlowAttributeChangeEvent event in two locations. When the flow executes the FlowAttributeChangeEvent in the set method of the attribute that drives the change, the component isn't in the DOM and the event essentially goes nowhere. Adding the event in the connectedCallback method ensures that when the flow adds the component to the DOM, the flow runtime can consume the event.

  3. Don't omit the FlowAttributeChangeEvent event in the set method. Firing the FlowAttributeChangeEvent in the set method of the attribute that drives the change ensures that changes are applied to both the attribute that drives the change and the derived attribute. Consequently, reactivity chains that include a custom component function properly.

  4. Don't omit the connectedCallback method by using Promise.resolve().then(...) to delay firing the FlowAttributeChangeEvent inside the set method. Delaying the FlowAttributeChangeEvent ensures that the component is in the DOM when the flow first calls the set method. However, the delay also means that the component fires the event after the flow initializes the screen. Consequently, the flow runtime always overrides preserved values and overrules the setting for revisited value.

In addition to FlowAttributeChangeEvent, your Lightning web components can fire navigation events to move between screens, pause, or finish flows. To work with navigation events, import these functions from lightning/flowSupport:

  • FlowNavigationNextEvent
  • FlowNavigationBackEvent
  • FlowNavigationPauseEvent
  • FlowNavigationFinishEvent

This snippet describes how to import and fire a navigation event. While the example uses FlowNavigationNextEvent, the same structure holds for each of the available navigation targets.

Navigation events allow your Lightning web components to include additional control mechanisms. Fire navigation events when reacting to end-user interactions. Refer to these guidelines when using navigation events:

  • Fire navigation events in event handlers (or in the method they invoke).
  • Don’t fire navigation events outside of event handlers because it leads to a poor user experience.
  • Don’t fire navigation events in lifecycle handlers like renderedCallback and connectedCallback.
  • Place the logic for skipping screens in Flow Decision Nodes, not in the lifecycle of a screen.
  • Don’t perform navigation in a dummy screen because it can lead to poor Flow performance.
  • If you encounter a scenario where navigation must be done within the initialization phase of the screen because data isn’t yet available to Flow Decision Nodes, post a request for an upgrade in Idea Exchange