Screen Flows, part of Salesforce Flow, lets developers and administrators create user interfaces and tie them to Salesforce data using code or no-code tools. Screen flows allow developers to use Lightning Web Components (LWC) as flow elements. However, building LWCs for a flow requires that you follow some best practices. Doing so increases the performance of your LWCs, and your component works better within the Flow runtime engine.

In this blog, we first dive into how state management works in the Flow runtime, and then share some state management and event best practices that you should follow when building LWCs for your screen flows.

Best practices for LWC state management in screen flows

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 and instead request that a change be made by firing FlowAttributeChangeEvents.

This pattern is essential for two reasons.

  1. Components that allow their parents to control their @api parameters are more easily reused because they allow the application to control the state rather than the component’s own internal business logic.
  2. By firing attribute change events, the Flow runtime can maintain an accurate picture of the component state, which allows it to control inter-component interactions, such as conditional field visibility.

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

Below is an example of a simple text input component. It demonstrates how a FlowAttributeChangeEvent should be fired in lieu of updating the LWC’s @api parameter.

Troublesome code

Compliant code

Best practices for LWC events in screen flows

In the upcoming sections, we deep dive into best practices for flow events like FlowAttributeChangeEvent and FlowNavigationEvent.

Attribute change event best practices

This section presents a list of FlowAttributeChangeEvent (see docs) Dos and Don’ts.

  1. Do not modify @api attributes in the component. Instead, fire the FlowAttributeChangeEvents event.
  2. Do fire the event in event handlers or in methods invoked within event handlers.
  3. Do limit the event’s value parameter to the following data types:
    1. String
    2. Number
    3. Boolean
    4. JSON (for record types)
  4. Do ensure that the data type of the FlowAttributeChangeEvent value parameter matches the data type of the LWC’s @api parameter.
  5. Do react to changes to @api parameters by using a get/set pattern where appropriate. This is also needed to cross-communicate between components when you are using reactive screen components (Beta).

The example below demonstrates using get/set to count the updates to an @api parameter vs. simply incrementing a counter before firing an attribute change event. It is worth noting that both the below patterns are valid but achieve slightly different results.

In the above scenario, this.changeCounter will be incremented whenever the user inputs a change in the text input and whenever the Flow runtime determines that the textValue property has changed. This will consider component initialization and cross-component interactions when using reactive screen components.

Note: an intermediate variable will need to be used to render updates from the set method. In this example, we introduce the textValueToRender for this purpose.

Let’s look at a code example where we increment a counter before firing an attribute change event. In this scenario, we do not use the get/set pattern.

In this case, this.changeCounter will be incremented whenever the user inputs a change in the text input.

6. Do use anotationless or @track parameters to maintain a modifiable local component state.

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

colorPicker.js

7. Do use a get method to combine multiple @api parameters together to construct a derived variable for the view.

Salutation.js

8. Do apply the same logic in the set method of every @api parameter that contributes to a derived member variable. This is needed for internal component state management for complex scenarios. This will ensure that a change made to any contributing attribute will cause the value to render correctly, regardless of the order in which attributes are set.

Dropdown.js

9. Do not modify FlowAttributeChangeEvent parameters after construction. By default, FlowAttributeChangeEvent instances are composed and bubble.

10. Avoid firing FlowAttributeChangeEvents and FlowNavigationXxx events at the same time. The rationale for this is it can lead to race conditions. There’s never a guarantee that the LWC component has had time to render the updated values by the time we start the navigation process.

Navigation change event best practices

In addition to the FlowAttributeChangeEvent, custom LWCs can fire navigation events to move between screens and pause or finish flows.

The intent of navigation events is to allow for custom LWC authors to include additional control mechanisms within their components. To that end, navigation events should be fired when reacting to end-user interaction.

Consult the list of Dos and Don’ts below when planning on using navigation events.

  1. Do fire navigation events in event handlers (or in the method they invoke).
  2. Do not fire navigation events outside of event handlers as it leads to a poor experience for the end user.
  3. Do not fire navigation events in lifecycle handlers like renderedCallback and connectedCallback.

The logic for skipping screens should rest in Flow Decision Nodes and should not be determined during the lifecycle of a screen. Additionally, navigation in a dummy screen can lead to poor flow performance.

Conclusion

We love it when developers create innovative solutions with Lightning Web Components combined with Flows. Following the best practices outlined in this blog ensures that your components integrate well within the Flow runtime engine and work as expected.

References

About the authors

Josh Goodman

Josh Goodman is a Principal Member of the Technical Staff with over 7 years of experience as a developer at Salesforce. Through his tenure, Josh has worked on many exciting features such as adding Reports and Dashboards to Salesforce One mobile, bringing Wave Charting to Lightning, adding nested folder support to the Reports and Dashboard home pages, and converting the Flow UI to from Aura to LWC. He is currently working on exciting new UI runtime features for Flows.
Mohith Shrivastava
Mohith Shrivastava is a Developer Advocate at Salesforce with a decade of experience building enterprise-scale products on the Salesforce Platform. He is presently focusing on the Salesforce Developer Tools, Flow, Apex, and Lightning Web Components at Salesforce. Mohith is currently among the lead contributors on Salesforce Stack Exchange, a developer forum where Salesforce Developers can ask questions and share knowledge. You can follow him via his Twitter @msrivastav13.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS