Lightning Web Components (LWC) provide a powerful and performant way to build dynamic and interactive Salesforce applications. But when dealing with multi-layered related data, performance, maintainability, and user experience become critical factors. A well-optimized LWC controller should:
- Efficiently retrieve and display multiple levels of related data (e.g., Accounts → Opportunities → Line Items).
- Dynamically refresh data when updates occur, ensuring real-time consistency.
- Use toast messages for clear user feedback on actions (success, error, or warnings).
In this post, we’ll walk through some best practices to build a high-performing LWC controller that dynamically loads related data, refreshes efficiently, and improves user experience with toast messages.
Define the Use Case & Data Model
Scenario:
We’re building an LWC that displays Accounts and their related Opportunities, which further contain Line Items. Users can update data at any level, and the component should automatically refresh without a full page reload.
Data Structure:
- Account (Parent)
- Opportunities (Child)
- Opportunity Line Items (Grandchild)
- Opportunities (Child)
Our goal is to efficiently fetch, update, and refresh this hierarchical data structure in LWC.
Query Data Efficiently Using Apex
Fetching multi-layered related data in a single Apex call reduces network requests and improves performance. Here’s how to optimize the SOQL query:
Apex Controller (AccountController.cls)
public with sharing class AccountController {
@AuraEnabled(cacheable=true)
public static List<Account> getAccountsWithOpportunities() {
return [SELECT Id, Name, (SELECT Id, Name, Amount, StageName,
(SELECT Id, Quantity, UnitPrice, TotalPrice, Product2.Name
FROM OpportunityLineItems)
FROM Opportunities)
FROM Account
WHERE Id IN (SELECT AccountId FROM Opportunity)];
}
@AuraEnabled
public static void updateOpportunity(Opportunity opp) {
update opp;
}
}
Key Optimizations:
- Single SOQL Query: Retrieves Accounts → Opportunities → Line Items in one request, reducing API calls.
- @AuraEnabled(cacheable=true): Enables client-side caching, reducing server load.
- WHERE Condition: Restricts data to Accounts that actually have Opportunities, improving efficiency.
Build the LWC Component
Component Structure:
accountList.html→ Renders the hierarchy (Accounts → Opportunities → Line Items).accountList.js→ Calls Apex methods, refreshes data, and handles UI interactions.accountList.js-meta.xml→ Defines metadata for Lightning App Builder usage.
LWC Markup (accountList.html)
<template>
<lightning-card title="Accounts and Opportunities">
<template if:true={accounts}>
<template for:each={accounts} for:item="account">
<lightning-accordion key={account.Id}>
<lightning-accordion-section name={account.Id} label={account.Name}>
<template for:each={account.Opportunities} for:item="opp">
<div key={opp.Id} class="slds-m-around_medium">
<lightning-tile label={opp.Name}>
<p>Stage: {opp.StageName}</p>
<p>Amount: {opp.Amount}</p>
<lightning-button
label="Update Stage"
data-id={opp.Id}
onclick={handleUpdateOpportunity}>
</lightning-button>
<template if:true={opp.OpportunityLineItems}>
<lightning-accordion>
<template for:each={opp.OpportunityLineItems} for:item="lineItem">
<lightning-accordion-section key={lineItem.Id} label={lineItem.Product2.Name}>
<p>Quantity: {lineItem.Quantity}</p>
<p>Price: {lineItem.UnitPrice}</p>
<p>Total: {lineItem.TotalPrice}</p>
</lightning-accordion-section>
</template>
</lightning-accordion>
</template>
</lightning-tile>
</div>
</template>
</lightning-accordion-section>
</lightning-accordion>
</template>
</template>
<template if:false={accounts}>
<p class="slds-text-color_error">No records found.</p>
</template>
</lightning-card>
</template>
Key UI Features:
- Accordion Layout: Helps users easily navigate through related records.
- Dynamic Data Rendering: Uses
for:eachloops to iterate through nested data. - Inline Updates: Includes a button to update Opportunity records.
LWC JavaScript Controller (accountList.js)
import { LightningElement, wire, track } from 'lwc';
import getAccountsWithOpportunities from '@salesforce/apex/AccountController.getAccountsWithOpportunities';
import updateOpportunity from '@salesforce/apex/AccountController.updateOpportunity';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { refreshApex } from '@salesforce/apex';
export default class AccountList extends LightningElement {
@track accounts;
wiredAccounts;
@wire(getAccountsWithOpportunities)
wiredAccountsHandler(result) {
this.wiredAccounts = result;
if (result.data) {
this.accounts = result.data;
} else if (result.error) {
this.showToast('Error', 'Failed to load data', 'error');
}
}
handleUpdateOpportunity(event) {
const oppId = event.target.dataset.id;
const updatedOpportunity = { Id: oppId, StageName: 'Closed Won' };
updateOpportunity({ opp: updatedOpportunity })
.then(() => {
this.showToast('Success', 'Opportunity updated successfully', 'success');
return refreshApex(this.wiredAccounts);
})
.catch(error => {
this.showToast('Error', 'Update failed', 'error');
});
}
showToast(title, message, variant) {
this.dispatchEvent(new ShowToastEvent({
title,
message,
variant
}));
}
}
Key Functionalities:
- @wire with Refreshable Data: Caches the Apex response and dynamically refreshes using
refreshApex(). - Toast Messages: Uses
ShowToastEventto display feedback messages when updates occur. - Inline Data Updates: Updates an Opportunity’s stage and auto-refreshes data to reflect changes.
Deploy & Test
Deployment Steps:
- Deploy the Apex Controller (
AccountController.cls) to your Salesforce org. - Deploy the LWC (
accountList) to Salesforce. - Add the LWC component to a Lightning Record Page or App Page.
Testing Scenarios:
- Verify that Accounts, Opportunities, and Line Items load correctly.
- Update an Opportunity’s stage and confirm the toast message appears.
- Ensure the data refreshes automatically after an update.
- Check that error handling works by testing with insufficient permissions.
Final Thoughts
In this post we went a bit deeper into the guts of designing and creating a LWC for Salesforce.com; including a few insights into best practice design:
A well-architected LWC controller enhances performance, user experience, and maintainability. By:
- Using optimized Apex queries to fetch multi-layered data in one call.
- Implementing dynamic refreshes with
refreshApex()to keep data up to date. - Providing toast messages to improve user feedback.
You ensure that your LWC not only functions efficiently but also delivers a seamless and interactive user experience.
Other Blog Links:
- Intro to Salesforce Best Practices Post
- Intro to Building A Basic LWC

We would love to hear your comments!