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
- Data retrieval
- Data caching
- Component Instantiation
- Base Lightning Components
- Image optimization
- Component lifecycle rendering and reflow
- Developer settings and tools
Lightning Web Components vs Aura Components
- Read Case Study: DreamHouse Gains Speed by Switching to LWC.
- Take the Trailhead module Lightning Web Components for Aura Developers.
- Review the Migrate Aura Components to Lightning Web Components developer guide.
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.
- 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
getRecordwire 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
getRecordUiunless absolutely required. The response includes metadata that is often 100-1000 times larger than the data payload. If you need only record data, use
- Read Data Guidelines in the Lightning Web Components Dev Guide.
- Read the Modularizing Code in Lightning Components blog post to learn more about service components.
- Take the Trailhead module Lightning Web Components and Salesforce Data.
- Review the pagination section from the Designing Lightning Pages for Scale blog post.
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.
- Review the Lightning Data Service section of the Lightning Web Components Dev Guide.
- Read the Caching and Synchronizing Component Data with Lightning Data Service blog post
- Review the Get Record Data and Understand the Wire Service section of the Lightning Web Components Dev Guide to learn more about the wire service.
- Watch the Wire an Apex Method to a Property video in our LWC Video Gallery.
- Review the getRecord wire adapter.
- Check out the following components in the DreamHouseApp.io (repo) to see the Lightning Data Service in action: mapCard, propertySummary, mortgageCalculator.
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
- See Data Guidelines in the Lightning Web Components Dev Guide.
- Review the Call Apex Methods section of the Lightning Web Components Dev Guide.
- See the Enable Client-Side Caching in the Call Apex Methods section of the Lightning Web Components Dev Guide.
- Check out the DreamHouse sample application for examples of storable actions in: similarProperties, propertyCarousel, propertyTileList.
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-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.
There are three methods to conditionally render your Lightning web components:
- Lightning App Builder dynamic component visibility
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.
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.
- Read more about Dynamic Lightning Pages.
- Review the Render DOM Elements Conditionally section of the developer guide.
- In the DreamHouse sample application see the propertyCarousel component which uses
if:true|falseto component render different elements based on if the property has images or not.
Lists should be created using either
iterator. The difference between these is that
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.
- Review the Render lists section of the Lightning Web Components Dev Guide to learn more about
- In the DreamHouse sample application check out propertyTileList component that uses
for:eachin combination with the paginator component to allow for interacting with long lists.
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
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
composed. Typically you should use events configured with
composed: falsebecause 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
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.
- Review the Communicate with Events and Configure Event Propagation sections of the Lightning Web Components Dev Guide to learn more.
- Review Communicate Across the DOM with Lightning Message Service (Beta) and this blog post to learn more about the new Lightning Message Service.
- Watch the Parent-Child Components video in our LWC Video Gallery to see how to send data from a child component to a parent component.
- In the DreamHouse sample application check out propertyTileList and paginator for an example of using the Lightning Message service to communicate between components.
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
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.
- 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-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
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.
- Review the Component Reference tab of the Lightning Web Components Dev Guide to learn more about the many components offered.
Whenever possible, use the (sprite-based) SLDS icons (using
<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.
- 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?”
- 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:
- 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.
- Review the Debug Lightning Web Components section of the Developer Guide.
- Watch the Debug LWC using browser-based tools video.
- Learn how to Enable debug mode for Lightning components.
- Learn more about Chrome Developer Tools.
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.