Back to insights

Lightning Web Components

A slow Lightning page is usually a data-design problem

JSBC Labs8 min read

The browser is often blamed too early

When a Lightning record page feels slow, teams commonly open the component JavaScript and look for an expensive loop. That can reveal a defect, but it is rarely the best first move. The larger cost is often architectural: several components request overlapping records, layouts return fields that nobody displays, an Apex endpoint duplicates data already available through Lightning Data Service, and every panel is created before the user needs it.

A component can be individually efficient while the composed page is wasteful. The unit of performance is therefore not the bundle or the method; it is the user journey across the whole page. Start with what the user is waiting to do, then trace every network request, cache interaction, component instantiation and re-render on the path to that moment.

Count data contracts, not component tiles

A page with twelve components does not necessarily have twelve data needs. Several may require the same Account name, owner or status. If each component independently calls Apex, the page accumulates latency, server work and separate failure modes. Salesforce's performance guidance recommends Lightning Data Service where possible, limiting returned fields and rows, paginating large results and avoiding data that the user may never open.

Create a page-level data inventory before tuning code. For each visible region, record the fields required for first interaction, the source, expected row count, cache behaviour, refresh trigger and owner. Overlap becomes obvious. So does speculative loading: secondary-tab history, rarely used related records and configuration metadata frequently arrive before the small set of values needed to make the page useful.

Treat the wire service as a subscription

Salesforce's Lightning Web Components documentation describes wire adapters as a reactive service that can emit new immutable data as configuration changes. The practical consequence is important: a wired handler is not a one-time fetch callback. It may receive cached data and later receive a fresher value, so displayed state must be derived safely from the latest emission rather than initialised once and forgotten.

This also changes how dependent reads should be designed. Keep a reactive parameter undefined until its upstream identifier is available, then allow the second wire adapter to activate. Do not assume that two independent adapters will deliver in a particular order, and do not change reactive configuration inside renderedCallback, where the resulting render-and-update cycle can repeat indefinitely. Subscription semantics should be visible in the component's design, not hidden behind fetch-shaped naming.

Choose one source of truth for a record

Lightning Data Service provides shared, security-aware client caching for supported UI API data. Apex can serve the cases LDS does not cover, including complex queries and transactional operations, and cacheable Apex can reduce repeat server work for non-mutating reads. Those mechanisms are useful, but they are not one coherent cache. Fetching the same record through LDS and Apex on the same page creates two copies with different refresh rules.

Pick the data access mechanism per record and responsibility. Prefer base components, UI API wire adapters or GraphQL wire capabilities when they satisfy the requirement and preserve platform-managed security and caching. Introduce Apex when the server must apply logic the UI APIs cannot express. Document that boundary so a future component does not add a convenient second path and quietly create inconsistent state.

Request less, then render later

Field selection is a performance decision. Request the explicit fields the first view needs instead of a complete layout whose contents can grow whenever an administrator changes configuration. Limit query results and paginate or virtualise long lists. If the browser already holds a suitably sized dataset, local sorting and filtering can avoid another round trip; if the dataset is large or security-sensitive, push the bounded query to the server instead.

Instantiation has a cost too. Defer components behind secondary tabs, accordions or conditional regions when the content is not needed for the first task. This is not permission to hide a badly designed query behind a click. It is a way to align expenditure with user intent. The initial view should load the smallest coherent set of data and interface elements that lets the user act confidently.

Make cache invalidation part of the write contract

Stale interfaces are often blamed on caching when the actual defect is an incomplete mutation contract. Updates performed through LDS-aware operations can participate in LDS synchronisation. When imperative Apex changes a record that LDS-backed components display, Salesforce directs developers to call notifyRecordUpdateAvailable after the write so active subscribers can receive fresh data. refreshApex belongs to wired Apex results, not LDS record adapters.

Write down the full sequence for every mutation: validation, server operation, success response, cache notification, optimistic or confirmed UI state, and failure recovery. Test it with sibling components showing the same record and with repeated updates. A save message is not proof of consistency; the contract is complete only when every visible representation converges on the committed state.

Set a performance budget around a user action

A generic instruction to make the page faster is not testable. Choose representative journeys and devices, then define what success means: time until the primary action is usable, number of initial data requests, payload size, long browser tasks and response under realistic network conditions. Compare the same measurements before and after a change, and retain them as a regression check rather than a one-off diagnostic.

Salesforce's own engineering guidance emphasises telemetry and early indicators for detecting Lightning performance regressions. Delivery teams need a smaller version of the same discipline. Track page-level performance alongside functional tests, investigate meaningful shifts and include composition changes in review. Adding one efficient component can still breach the page budget if it introduces another request, another large payload and another synchronous render dependency.

The JSBC Labs view

LWC performance is primarily a data-flow and composition problem. Begin with the user's first useful action. Inventory the page's data contracts, standardise each record on one source of truth, use subscription-safe state, request only what is needed and make invalidation explicit after writes. Optimise JavaScript once evidence shows that JavaScript is the constraint.

The strongest performance review does not ask whether every component is fast in isolation. It asks whether the page behaves like one designed application: shared data is actually shared, secondary work waits, mutations converge and regressions are measurable. That is how a collection of technically correct components becomes an experience users can trust at production scale.

Official references

Continue reading