This is an update from a 2017 Post.

Lightning web components run on the client-side, in a single page, where they are created and destroyed as needed, alongside other components that work on the same data. We will discuss how these characteristics impact performance, and review a list of best practices to optimize the performance of your Lightning Components. Our original Performance Best Practices blog post from 2017 focused on Aura components. A lot can change in three years, and in this updated post we’ll be focusing on Lightning web components.

If you are just starting out with Lightning Web Components, consider completing the Build Lightning Web Components trail, watching some of the topic specific videos in the Lightning Web Components Video Gallery and checking out some of our sample apps in the Sample Gallery before diving deeper.

Note: The name of the framework is Lightning Web Components (LWC), uppercase. When we talk about the Lightning web components themselves, we use lowercase.

The list of performance best practices discussed in this document includes:

Lightning Web Components vs Aura Components

Lightning web components align closely with modern web standards, which makes them more efficient for developers to build with and for browsers to render. The LWC bundle has a simpler structure than that of Aura components, and the development of LWC feels much more like other modern JavaScript frameworks. This can increase developer productivity. And not only is it easier for developers to build with LWC, but they can also increase the performance of applications when compared to Aura components. In one case study, Aura components migrated to LWC showed anywhere between a 2-60% decrease in Experience Page Time (EPT). So when it comes to making a choice between LWC and Aura, LWC is usually the recommended option. While there are still some cases where Aura components are required, we’re working hard to advance the LWC framework so that this will no longer be the case.

More information:

Data Retrieval

A component with no data is not a very useful component. With Lightning Web Components, you have various options available to you when retrieving data from the server. To optimize all of your server round-trips, keep these things in mind:

  • Use the Lightning Data Service or cache data whenever possible (see Data caching).
  • Before making a call to the server, make sure there’s no other option to obtain the data.
    • When appropriate, consider passing data between components using attributes, events, or methods rather than retrieving the same data in different components.
    • If multiple components on a given page retrieve the same data, consider creating a service component that has no UI elements and can query data once so that it can pass data on to other components.
  • When making a call to the server, limit the fields and rows of the result set:
    • Only SELECT the fields you need.
    • Set a LIMIT on the query, don’t return huge numbers of rows at once.
    • Implement pagination when queries may have large results sets.
  • Lazy load occasionally accessed data. Don’t preload data that the user may never need. For example, put least accessed components in a secondary tab that the user may not click.
  • Don’t make a call to the server to filter or sort data you already have at the client-side. JavaScript Arrays have built in functions to do things like sort, filter and find values.
  • As opposed to using Apex, leverage the Lightning Data Service and UI API to not only retrieve records, but also retrieve list views, metadata and picklist values.
  • When using the getRecord wire adapter (part of the UI API), request only the fields that the component requires. Be explicit. For example, if the component requires one field, request only that field.

Don’t request a record by layout unless you absolutely need all that data. Layouts are a collection of fields managed by the administrator and may change at any time. Layouts are very expensive because they often contain hundreds of fields.

Additionally, don’t using getRecordUi unless absolutely required. The response includes metadata that is often 100-1000 times larger than the data payload. If you need only record data, use getRecord.

More information:

Data Caching

Application composition is a powerful way to build apps by assembling self-contained components. However, without proper planning, the autonomous nature of the components you assemble can have an adverse impact on performance. For example, if all the components you build make their own isolated calls to the server to retrieve the data they need, you’ll have many calls to the server. It’s much more efficient to make a single larger call than to make many smaller calls.

Client-side data caching improves performance by sharing data among components, which significantly reduces the number of server round-trips. LWC has two built-in mechanisms for client-side caching: Lightning Data Service and cacheable Apex methods. If neither of these work for your purposes, you can also implement a custom caching solution.

Lightning Data Service

Lightning Data Service provides a managed record approach: you’re not responsible for writing data access logic, so no Apex here. It also handles security for you by checking the accessibility of records and fields. The framework is responsible for managing records; this includes fetching them from the server when requested the first time, storing them in a highly efficient client cache, sharing them between all components that request them, and sending changes to the server and invalidated cache entries when dependent Salesforce data changes.

Lightning Data Service can also handle progressive loading by allowing developers to specify which fields to load. When using the getRecord wire adapter, be sure to only request the needed data since it does allow developers to return entire page layouts (which may include unneeded data). Don’t use the getRecordUi wire adapter unless you absolutely need metadata; as we mentioned, its payload can be 100-1000 times larger than a data-only payload. Always request only the data you need and nothing more.

If another component subsequently requires additional fields, these fields are loaded transparently and added to the record in the cache. Unlike storable actions that can cache any type of response returned by an Apex method, Lightning Data Service caches many  types of User Interface API data: records, schema, metadata, layout metadata, lists of records, list metadata, and so on. The Lightning Data Service also improves user interface consistency: when a component updates a record, all the other components using that record are notified and in most cases refreshed automatically.

More information:

Cacheable Apex Methods

If you can’t use Lightning Data Service, use Apex. For specific use cases, see Data Guidelines in the Lightning Web Components Dev Guide.

A Cacheable Apex method is a server action whose response is stored in the client cache so that subsequent requests for the same server method with the same set of arguments can be accessed from that cache instead of the server. Cacheable Apex methods enable you to access data using a traditional remote procedure call (RPC) approach; you implement some logic in Apex that you expose as a remotely invocable method. Cacheable Apex methods enable you to cache virtually anything, whatever the server method call returns. The general guideline is to cache (mark as storable) any action that is idempotent and non-mutating.

Creating a Cacheable Apex method is easy. Simply annotate your Apex methods with @AuraEnabled(cacheable=true)

More information:

Component Instantiation

Showing every available piece of data and every available tool on the screen just in case the user may need it is generally not considered a good UX practice. It can also significantly impact the performance of your application. Lightning Components added to a page layout are instantiated when the page is loaded, contributing to the overall loading time of the page. Today’s interactive design guidelines favor progressive disclosure.

“Progressive disclosure is an interaction design technique often used in human computer interaction to help maintain the focus of a user’s attention by reducing clutter, confusion, and cognitive workload. This improves usability by presenting only the minimum data required for the task at hand” (Wikipedia).

Put another way, “progressive disclosure defers advanced or rarely used features to a secondary screen, making applications easier to learn and less error-prone” (Jakob Nielsen).

In Lightning Experience, it’s easy to implement progressive disclosure and defer data or features that aren’t essential to the task at hand. There are several approaches to defer component creation, let’s take a look at two of them in detail: Lazy instantiation (sometimes referred to as lazy loading) and conditional rendering.

Lazy Instantiation in Lightning Experience

Lightning App Builders can implement progressive disclosure declaratively by placing components within specific areas in the Lightning Experience where they are lazily instantiated. These areas include:

  • Standard tabs components allow information to be hidden and only loaded when the user selects that tab.
  • Lightning Component Actions or Quick Actions allow components to be loaded only when the user clicks on a button.
  • Utility Bar can be added to any app and also includes buttons that can load components when the user clicks on them.

Lazy Instantiation in Your Own Components

Leverage the set of tab components like <lightning-tabset> and <lightning-tab> that support lazy instantiation by default.

Note: TabSet and the App Builder tabs component are lazy-loaded however the tabs within the Lightning Console are loaded as workspaces and sub-tabs are not lazy-loaded.

Conditional Rendering

There are three methods to conditionally render your Lightning web components:

  1. Lightning App Builder dynamic component visibility
  2. if:true|false
  3. CSS

The first method is declarative and built directly into App Builder. Dynamic component visibility can control when a component appears on a Lightning page by adding filter conditions and logic to its properties in the Lightning App Builder. For example, you can construct a filter that causes a rich text component on an opportunity page to display when the opportunity’s amount is greater than or equal to $1 million.

The second method allows developers to conditionally render DOM elements using if:true|false to lazily instantiate parts of the UI:

In this case, if “something” = true, the first <div> tag and all its children are created, while the second <div> tag and all its children, are not rendered. This would swap when the “something” expression changes to false. In this situation, the first <div> tag would be destroyed and the second would be rendered.

The third method leverages CSS styles to toggle visibility and can be implemented using the following template and JavaScript.

Template:

JavaScript:

The <div> component and all of its children are created and rendered up front, but are hidden from the user until the JavaScript is run. Keep in mind that using CSS creates the component upfront so there’s no performance gain on page load like there is with methods one and two. However, when the JavaScript is run, the component displays immediately to the user without the need to initialize or render.

An important difference between CSS hiding and if:true/false is that with the CSS approach, the component stays alive and so its state is maintained. Using if:true|false destroys and recreates the component so state is lost (reset).

Use these methods in the order presented. The first option should be the declarative option discussed in method one, though this is only available on components configured for Lightning App Builder. Method two’s if:true|false approach should be your second option and works with all components. Both methods help your pages load faster initially by deferring the creation and rendering of the component or enclosed element tree until the condition is fulfilled. The third method may be useful when developers want to preload a component and then show it in the UI when the condition is fulfilled.

More information:

Lists

Lists should be created using either for:each or iterator. The difference between these is that iterator has first and last properties that let you apply special behaviors to the first and last items in an array.

When creating custom list components, don’t support the creation of an infinite number of list items. This can severely hamper performance, especially in large orgs with lots of records. Either provide a pagination mechanism, or virtualize the list (reuse and rehydrate a limited number of list item components).

Each list element must have a key whose value is unique across all children.

More information:

  • Review the Render lists section of the Lightning Web Components Dev Guide to learn more about for:each or iterator.
  • In the DreamHouse sample application check out propertyTileList component that uses for:each in combination with the paginator component to allow for interacting with long lists.

Events

Leveraging events is a great way to communicate between components and even allows developers to listen for events within the DOM as Lightning web components dispatch standard DOM events. Components can also create and dispatch custom events. Use events to communicate up the component containment hierarchy. Event listeners can be attached declaratively or programmatically using addEventListener().

Keep the following in mind when leveraging events and event handlers:

  • Minimize the number of event handlers to only those absolutely required. Each handler requires overhead and if too many are created it can slow the performance of your app.
  • Be sure to understand event propagation of parent-child components using bubbles and composed. Typically you should use events configured with bubbles: false and composed: false because they’re the least disruptive. These events don’t bubble up through the DOM and don’t cross the shadow boundary. If you create an event with bubbles: true and composed: true, you create an API contract for the component emitting the event.
  • To communicate between sibling components within a single Lightning page or across multiple pages, you can use Lightning Message Service. It has the advantage of working across Visualforce, Aura, LWC, utility bar components and across page tabs in a console app.
  • If you add a listener to something that is not part of the component lifecycle (like the window object, the document object, and so on), you’re responsible for removing the listener yourself. To remove the event listeners use removeEventListener() in the disconnectedCallback lifecycle hook. Failing to do so can result in memory leaks, which will progressively degrade the performance of the entire Lightning app, until the user closes or refreshes their browser tab.
  • When working with lists, letting events bubble, and registering a single event listener on a parent element instead of a separate event listener on every list item can significantly reduce the number of event listeners in your application. This can have a positive impact on performance.

More information:

Third-Party JavaScript Libraries and Style Sheets

Whenever possible, remove dependencies on unneeded libraries. Furthermore, before you decide to use a third-party library in a Lightning component, you should reevaluate if you really need that library. DOM manipulation libraries (like jQuery) and UI libraries (like Bootstrap or jQuery UI) in particular may no longer be needed when working with LWC. Using third-party or your own Custom Style Sheets should only be used if Salesforce Lightning Design System (SLDS) doesn’t meet your needs.

DOM Manipulation Libraries

JavaScript has come a long way in recent years. Many DOM manipulation utilities we couldn’t live without like jQuery are now standard in the language. Modern frameworks like Lightning Web Components also provide abstractions that make jQuery less relevant.

UI Libraries

You may want to avoid the use of UI libraries like Bootstrap and jQuery UI. Although these libraries provide useful components, they have their own UI identity that can clash with the Lightning Experience identity. The base Lightning components and the SLDS offer similar capabilities while providing a consistent user experience.

MVC (Model-View-Controller) Frameworks

At a high level, libraries like React and AngularJS have the same focus as the Lightning Web Components framework: They provide code organization and utilities to create components. Using another MVC framework together with LWC inside a component isn’t recommended. You can, however, use a Lightning component as a container to host components built with other frameworks (like React and AngularJS) inside the Lightning Experience. However, that is outside the scope of this document. See the LockerService and Lightning Container Component: Securely Using Third-Party Libraries in Lightning Components blog post for details.

Custom Style Sheets

Using third-party CSS style sheets or creating our own styles can cause performance issues and may make your UI look inconsistent to end users. Developers should become familiar with the Salesforce Lightning Design System (SLDS) that was developed by Salesforce to create beautiful and rich UI experiences for end users. It’s also much more than just styles and CSS, it includes design guidelines and principles used by our own engineers and component blueprints that cover common developer needs like Breadcrumbs, Modals, Alerts and much more. It has an incredible list of Design Tokens that store visual design attributes for color, fonts, spacing, sizing and even touch.

Levering the SLDS can also save developers time when building components since it’s built right into Lightning and doesn’t require developers to create and maintain their own CSS.

Use Minified Versions of Libraries and Style Sheets

If you absolutely need to use a third-party library, make sure you use minified versions of the library and style sheet to increase the performance of your app.

More information:

  • Review the Salesforce Lightning Design System (SLDS) to learn how to best use its styling in custom components.
  • See the libsChartjs component in the LWC Recipes app for an example of Chart.js in Lightning components.

Base Lightning Components

Before you start building your own custom Lightning components you should familiarize yourself with the library of base components offered. These include lightning-input-field, lightning-record-form, and many more. Leveraging these base components can significantly speed up your development time. For example if you want a big red button presented to a user, simply use the lightning-button:

To produce this:

Additional benefits include:

  • Styles: Base Lightning components are styled with the native Lightning look and feel.
  • Performance: Base Lightning components are already loaded at the client-side and don’t require additional download or processing. Our performance optimization efforts are also focused on components in the lightning namespace.
  • Responsiveness: By default base Lightning components are responsive in their design.
  • Innovation: The Lightning namespace is where components are being actively developed. This is where you can expect to see new and improved components moving forward.
  • Accessibility: Base Lightning components are built for accessibility.
  • Client-side validation: Base Lightning components include client-side validation when applicable.

More information:

  • Review the Component Reference tab of the Lightning Web Components Dev Guide to learn more about the many components offered.

Image Optimization

Whenever possible, use the (sprite-based) SLDS icons (using <lightning-icon> and <lightning-button-icon>) instead of custom icons. Salesforce has hundreds of icons for you to choose from, so before spending time creating your own custom icons, reuse the ones that SLDS offers. To give you a quick sample here are a few of our favorites:

When using other images, be sure to lock image dimensions to avoid reflows and serve the image in those dimensions when possible. For example, don’t load a high-resolution image to display a thumbnail.

More information:

  • Review the icons section of the SLDS guide to see all available icons.

Component Lifecycle Rendering and Reflow

Lightning web components have a lifecycle managed by the framework. The framework creates components, inserts them into the DOM, renders them, and removes them from the DOM. Read the rendering lifecycle to understand what happens during this lifecycle and which methods fire at what time. Minimize the number of times your component is being re-rendered. In some cases, it may be helpful to lock DOM regions to specific dimensions to avoid browser reflows of surrounding areas.

Development Settings and Tools

This section covers settings and tools that can aid a developer when building high performance applications. The best practices here are focused on helping developers work faster and more efficiently when building in Lightning.

Debug Mode & Component Caching

Optimizations for a production environment and a development environment are different. Here is a typical question asked in the developer forums:

“When I make changes in a component, I need to refresh the page a few times in the browser for my changes to take effect. Why do I have to do that, and how can I avoid it?”

The reason is that to optimize performance in a production environment, component definitions are cached at the client-side. As a result, when you make a change to a component, and then reload the page hosting the component, you can still get the previous version of the component served from the client cache. You can usually get the new version of the component by refreshing the page a couple of times, but that isn’t a suitable developer experience. The solution is to enable debug mode and disable client-side caching during development. Debug mode also makes it easier to debug JavaScript code from Lightning components as framework JavaScript code isn’t minified and is easier to read.

Recommended settings:

Development Production
Debug mode On Off
Component caching Off On
  • To enable debug mode: Setup > Custom Code > Lightning Components > Debug Mode. Select the users you want to enable debug mode for.
  • To disable component caching: Setup > Security > Session Settings > Caching, and uncheck Enable secure and persistent browser caching to improve performance

IMPORTANT NOTE: Client-side caching is an org-wide setting. Disabling client-side caching and enabling debugging has a significant impact on performance. Only do this in your development environment, not in a production environment. Similarly, if you run tests to assess the performance of your environment, remember to turn these settings back to the production values before running those tests.

Performance Profiling & Debugging Tools

If you face a problem in a component, make sure you understand what the real problem is before trying to solve it. Don’t make assumptions. Use profiling tools to identify the bugs and performance bottlenecks.

  • You can leverage Chrome’s built in DevTools to help in a number of areas:
    • You can use them to step through JavaScript as it runs to help debug issues.
    • Examine network traffic using the Network panel to watch when the server method is called and when it’s not, for example when working with a storable action.
    • Profile component performance with the Performance panel to watch which actions or requests are taking the most time.
    • Note that is helpful to run Chrome in Guest or Incognito mode when leveraging the developer tools to reduce any noise created by Chrome Extensions
  • Use the Salesforce Lightning Inspector Chrome Extension which is similar to the Performance panel.
  • Analyze with the Salesforce Community Page Optimizer for working with communities.

More Information:

Summary

The performance of an application is impacted by many different factors. The Lightning Web Components performance optimization techniques described in this article are general guidelines that should help you build faster and more responsive applications. Try them out in your application, and let us know what your favorite performance optimization techniques are. If you’re curious about how to assemble blazing fast Lightning pages at scale, check out the Designing Lightning Pages for Scale blog post.

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

Add to Slack Subscribe to RSS