ESC

AI-powered search across all blog posts and tools

Flows Β· February 2, 2026

Screen Flow Performance - From Slow to Lightning Fast

Practical techniques to eliminate the lag, reduce server round-trips, and build screen flows users actually enjoy.

☕ 8 min read 📅 February 2, 2026
  • Every Get Records element that runs before a screen is displayed adds a server round-trip β€” eliminate unnecessary ones with formulas
  • Reactive screens process component interactions client-side with no server call, dramatically improving perceived performance
  • Choice set efficiency and collection pre-loading are the most underused performance levers in complex screen flows

I rebuilt a 12-screen onboarding flow last year. The original took 45 seconds to complete β€” not because there were 12 screens, but because of how it was built. After optimization it took 11 seconds. Same logic, same screens, dramatically different experience.

Screen flow performance isn’t mysterious. There are a handful of well-understood patterns that cause slowness, and a matching set of techniques that eliminate them. Let me walk through what I’ve learned.


Where Screen Flows Lose Time

Before optimizing, it helps to understand where time actually goes in a screen flow execution.

Screen Flow Execution Timeline
Screen Flow: Time Breakdown (Example)Initial LoadGet Records (x3)Screen RenderUser InputDML Operations~800ms~2,200ms ← biggest offender~600msUser-controlled (variable)~900msAfter: ~600msforcenaut.com β€” Screen Flow Execution Timeline

The data retrieval phase dominates. Every Get Records element before a screen is displayed causes a server round-trip. Three Get Records before the first screen means three sequential calls to the database before the user sees anything.


The Problem

A 12-screen onboarding flow took 45 seconds end-to-end. Profiling in Debug mode revealed 3 Get Records elements running sequentially before the first screen β€” querying Account, primary Contact, and a related custom config object independently. Each took 700-900ms. Users were staring at a spinner for 3+ seconds before seeing screen one.

The Solution

The config object query was replaced with a Formula resource (the value was derivable from the Account’s Industry field). The Account and Contact queries were merged into one Get Records using a relationship query. First-screen load time dropped from 3.1 seconds to under 0.9 seconds with no logic changes.

Rule 1: Collapse Pre-Screen Get Records

The most impactful change I make in almost every optimization: reduce the number of Get Records elements that execute before the first screen.

Before: Three separate Get Records elements, each querying a different object.

After: One Get Records that queries the primary object with all needed fields, then use Formula resources and collection filters to derive the rest.

Ask yourself for each Get Records: is this value something I could compute from data I already have? Could it be a formula? Could it be merged with another query?

ℹ️ Info

If two Get Records queries are independent of each other, Salesforce does not parallelize them β€” they run sequentially. The fewer, the faster.


Rule 2: Move Formulas in Front of Get Records

Formula resources evaluate in memory with zero latency. Before adding a Get Records, ask: can I derive this with a formula?

Examples of what formulas can replace:

  • Calculating a due date from today + a constant
  • Constructing a record name from other variables
  • Determining which approval path to take based on the triggering record’s fields
  • Formatting a currency value for display

A formula that runs in 0ms beats a Get Records that runs in 400ms. Use them aggressively.


Rule 3: Reactive Screens β€” Zero Round-Trips for Dependent Fields

Reactive Screens is one of the most underused performance features in Screen Flows. When enabled, interactions between components on the same screen happen entirely client-side β€” no server call.

How it works

Normally, when a user changes a picklist that controls the visibility of another field, the flow sends a request to the server to re-evaluate conditions and re-render affected components. With reactive screens, this evaluation happens in the browser.

⚠️ Reactive Screen Limitations
  • Not all standard screen components support reactive behavior yet
  • Custom LWC components need to be built with lightning-flow-support to participate
  • Formulas on reactive screens must use only screen-local variables (no cross-screen references)

For wizard-style flows where users make selections that affect visible fields on the same screen, reactive screens eliminate the most noticeable latency users experience.


Rule 4: Choice Sets vs. Dynamic Choice Sets

Static Choices

Static choices β€” hardcoded options, zero server cost. Best for options that rarely change.

Picklist Choice Sets

Choice sets from object picklist fields β€” fetched once, lightweight. Good for standard picklist values.

Dynamic Choice Sets

Dynamic choice sets from Get Records β€” runs a SOQL query every time the screen loads. Use only when options genuinely vary per record or user.

Dynamic choice sets are often used unnecessarily. If the options don’t change record-by-record (e.g., a list of all active Product Categories), consider whether a static choice set would serve equally well β€” it loads instantly.

Efficient Dynamic Choice Pattern

If you do need dynamic choices, pre-load the data with a single Get Records early in the flow and store it in a collection variable. Then reference that pre-loaded collection in your Dynamic Choice Set, rather than letting the choice set trigger its own query on each screen render.

// Flow structure for efficient dynamic choices:
// 1. [Get Records: All Active Products β†’ Products_Collection]
// 2. [Screen 1 - uses Products_Collection for choice set]
// 3. [Screen 2 - reuses Products_Collection, no new query]

Rule 5: Minimize Data Passed Between Screens

Every variable held in flow memory is serialized and passed to the server on each navigation between screens. Large collection variables β€” a list of 500 records, for example β€” add serialization overhead on every Next button click.

Best practice: only load what you need for the current and next screen. If a later step needs data that depends on user input from an earlier step, do that Get Records after the relevant screen, not before the first screen.


Rule 6: Lazy Loading with Conditional Paths

Don’t load data for a path the user might not take. If your flow has a branch where 80% of users go left and 20% go right, and the right branch needs a complex Get Records β€” put that Get Records inside the right-branch path, not before the Decision element.

Correct: Lazy Loading

[Screen: Which path?]
    ↓ User selects "Advanced Options"
[Get Records: Advanced config data]  ← Only loads when needed
    ↓
[Screen: Advanced Options screen]

Anti-Pattern: Eager Loading

[Get Records: Advanced config data]  ← Loads for ALL users even if they never reach it
[Screen: Which path?]

This is especially wasteful for multi-step wizards where early screens collect the information needed to determine which subsequent data to load.


Rule 7: Assignment Instead of Get Records for Derived Values

A pattern I see frequently: a Get Records that queries a single record purely to read one field value that could have been passed in as an input variable.

If a screen flow is launched from a record page (via a Quick Action or Utility Bar), use the record’s Id as an input variable and pass in the fields you need as additional input variables from the launching context. The Flow Orchestration and Quick Action framework support this.

This avoids the Get Records entirely β€” the data is already available at flow start.


Rule 8: DML Consolidation at the End

Every Create Records or Update Records element is a DML statement and a server round-trip. If your flow creates 3 records across 3 separate elements, that’s 3 DML operations.

DML Consolidation Pattern

Better pattern: use Assignment elements to build up records into collection variables throughout the flow, then do a single Create Records or Update Records at the end with the full collection.

// Build up during flow:
[Assignment: Add Contact to New_Contacts_Collection]
[Assignment: Add Case to New_Cases_Collection]

// Single DML at completion:
[Create Records: New_Contacts_Collection]
[Create Records: New_Cases_Collection]

Two DML operations instead of potentially many, and the user reaches the success screen faster.


πŸ’‘ Always Profile Before You Optimize

It’s tempting to start removing Get Records elements the moment a flow feels slow. But in practice, the bottleneck is often not where you expect. Run the flow in Debug mode first and expand each element to check its execution time. I’ve spent an hour refactoring Get Records only to discover the real culprit was a custom LWC component making its own Apex callout on load. Profile first, then fix the actual slowest element. Repeat until it’s fast.

Rule 9: Profile Your Flow in Debug Mode

The Flow Debug panel shows exactly how long each element took to execute. Before optimizing blindly, run the flow in debug mode and identify the actual bottleneck. I’ve been wrong about where time was being spent more than once.

Steps:

  1. Open the Flow in Builder
  2. Click Debug
  3. Fill in test values and run
  4. Expand each element in the debug output to see execution time

Sort by time spent. Fix the slowest element first. Repeat.


Rule 10: LWC Components in Screen Flows

If your Screen Flow embeds custom Lightning Web Components, those components are subject to their own performance considerations:

  • Use @wire adapters instead of @AuraEnabled imperative calls where possible
  • Avoid making Apex calls in connectedCallback if the data can be passed in as a Flow input variable
  • Implement lightning-flow-support for reactive behavior
  • Keep component state minimal β€” large component state serializes across screen transitions
⚠️ Warning

A poorly optimized LWC in a screen flow can add 1-2 seconds per screen independently of everything else in the flow.


The Optimization I Run Every Time

When I take over a slow screen flow, I do these five things in order:

  1. Run in debug mode and identify the top 3 slowest elements
  2. Move any Get Records that occur before the first screen β€” can any be removed or combined?
  3. Replace Get Records with formulas wherever the data can be derived
  4. Enable Reactive Screens and verify dependent fields work correctly
  5. Consolidate DML into collection operations at the end

In my experience, steps 2 and 3 alone account for 60-70% of the performance improvement in most flows.

What’s the slowest screen flow in your org right now, and what do you think the main culprit is? If you’ve profiled it in Debug mode and found a surprise bottleneck, share it in the comments β€” those real-world examples are always the most useful.


Knowledge Check

A screen flow has 3 Get Records elements before the first screen. Each takes ~800ms. What is the most effective optimization?
When should you use a Dynamic Choice Set instead of a Static Choice Set in a Screen Flow?

How did this article make you feel?

Comments

Salesforce Tip

🎉

You finished this article!

What to read next

Contents