LWC Data and Platform
Wire Adapters
Read this page in order: wire fundamentals first, Lightning Data Service access patterns second, then message channels and navigation.
getRecord
Usage: Use getRecord to fetch a single record's data from Lightning Data Service.
Guidelines:
- Define fields as constants at the top of the file
- Handle both data and error cases
- Use reactive parameters with
$prefix for dynamic updates
Examples:
import { wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
const ACCOUNT_FIELDS = [
'Account.Id',
'Account.Name',
'Account.Industry',
'Account.AnnualRevenue'
];
export default class AccountDetail extends LightningElement {
@api recordId;
account;
error;
@wire(getRecord, { recordId: '$recordId', fields: ACCOUNT_FIELDS })
wiredAccount({ error, data }) {
if (data) {
this.account = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.account = undefined;
this.handleError(error);
}
}
handleError(error) {
// Handle error appropriately
console.error('Error loading account:', error);
}
}
getRecordUi
Usage: Use getRecordUi to fetch record data with UI metadata (layout, picklist values, etc.).
Examples:
import { wire } from 'lwc';
import { getRecordUi } from 'lightning/uiRecordApi';
export default class AccountForm extends LightningElement {
@api recordId;
recordUi;
error;
@wire(getRecordUi, { recordIds: '$recordId', layoutTypes: 'Full', modes: 'View' })
wiredRecordUi({ error, data }) {
if (data) {
this.recordUi = data.records[this.recordId];
this.error = undefined;
} else if (error) {
this.error = error;
this.handleError(error);
}
}
}
Wire to Apex Methods
Usage: Use @wire with Apex methods for reactive data fetching.
Guidelines:
- Use
cacheable=truefor read-only methods when possible - Handle errors appropriately
- Use reactive parameters with
$prefix
Examples:
import { wire } from 'lwc';
import getAccountData from '@salesforce/apex/AccountController.getAccountData';
export default class AccountData extends LightningElement {
@api recordId;
accountData;
error;
@wire(getAccountData, { accountId: '$recordId' })
wiredAccountData({ error, data }) {
if (data) {
this.accountData = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.handleError(error);
}
}
handleError(error) {
// Handle error - show toast, log, etc.
console.error('Error loading account data:', error);
}
}
Apex Method (for reference):
@AuraEnabled(cacheable=true)
public static Account getAccountData(Id accountId) {
return [SELECT Id, Name, Industry FROM Account WHERE Id = :accountId];
}
Wire Error Handling
Guidelines:
- Always handle wire errors appropriately
- Provide user feedback for errors (toast notifications, error states)
- Log errors for debugging
- Don't leave error states unhandled
Examples:
@wire(getRecord, { recordId: '$recordId', fields: ACCOUNT_FIELDS })
wiredAccount({ error, data }) {
if (data) {
this.account = data;
this.error = undefined;
this.isLoading = false;
} else if (error) {
this.error = error;
this.account = undefined;
this.isLoading = false;
this.handleWireError(error);
}
}
handleWireError(error) {
// Show toast notification
this.showToast('Error', 'Failed to load account data', 'error');
// Log error for debugging
console.error('Wire error:', error);
// Set error state for UI
this.hasError = true;
}
Lightning Data Service
Use this section for LDS-specific guidance that complements the wire fundamentals above.
Choosing getRecord vs getRecordUi
- Use
getRecordwhen you need specific fields for component logic - Use
getRecordUiwhen you also need layout/metadata context (for example dynamic form behavior) - Default to
getRecordunless metadata is required, because payloads are usually smaller and simpler to consume
Field Access Patterns
Accessing Field Values:
// Good: Access field value with null check
if (data && data.fields && data.fields.Name) {
this.accountName = data.fields.Name.value;
}
// Good: Use optional chaining (if supported)
this.accountName = data?.fields?.Name?.value;
// Good: Provide default value
this.accountName = data?.fields?.Name?.value || 'Unknown';
Message Channels
Use this section for Lightning Message Service communication patterns. Keep LMS setup, publishing, and subscription logic together here.
MessageContext Pattern
Usage: Use MessageContext with @wire to access the Lightning Message Service context.
Examples:
import { wire } from 'lwc';
import { MessageContext } from 'lightning/messageService';
export default class MessageComponent extends LightningElement {
@wire(MessageContext)
messageContext;
}
Publishing Messages
Usage: Use publish to send messages to subscribed components.
Examples:
import { publish } from 'lightning/messageService';
import SELECTED_CHANNEL from '@salesforce/messageChannel/exampleChild__c';
export default class MessagePublisher extends LightningElement {
@wire(MessageContext)
messageContext;
handleClick() {
if (this.messageContext) {
publish(this.messageContext, SELECTED_CHANNEL, {
identifier: this.uniqueId,
action: 'clicked'
});
}
}
}
Subscribing to Messages
Usage: Use subscribe to listen for messages from other components.
Examples:
import { subscribe, unsubscribe } from 'lightning/messageService';
import SELECTED_CHANNEL from '@salesforce/messageChannel/exampleChild__c';
export default class MessageSubscriber extends LightningElement {
@wire(MessageContext)
messageContext;
subscription;
connectedCallback() {
this.subscribeToMessage();
}
subscribeToMessage() {
if (this.messageContext) {
this.subscription = subscribe(
this.messageContext,
SELECTED_CHANNEL,
(message) => this.handleMessage(message)
);
}
}
handleMessage(message) {
// Handle received message
if (message.identifier === this.targetId) {
this.handleAction(message.action);
}
}
disconnectedCallback() {
if (this.subscription) {
unsubscribe(this.subscription);
}
}
}
Message Channel Best Practices
- Always unsubscribe in
disconnectedCallbackto prevent memory leaks - Check if
messageContextexists before publishing/subscribing - Use meaningful message payloads with clear structure
- Document message channel usage in component documentation
Navigation Patterns
NavigationMixin Usage
Usage: Use NavigationMixin to navigate to different pages in Salesforce.
Examples:
import { NavigationMixin } from 'lightning/navigation';
export default class NavigationComponent extends NavigationMixin(LightningElement) {
handleNavigateToRecord() {
this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: {
recordId: this.recordId,
actionName: 'view'
}
});
}
handleNavigateToObjectHome() {
this[NavigationMixin.Navigate]({
type: 'standard__objectPage',
attributes: {
objectApiName: 'Account',
actionName: 'home'
}
});
}
handleNavigateToWebPage() {
this[NavigationMixin.Navigate]({
type: 'standard__webPage',
attributes: {
url: 'https://example.com'
}
});
}
handleGenerateUrl() {
this[NavigationMixin.GenerateUrl]({
type: 'standard__recordPage',
attributes: {
recordId: this.recordId,
actionName: 'view'
}
}).then((url) => {
// Use generated URL
window.open(url, '_blank');
});
}
}
Navigation Types
Common Navigation Types:
standard__recordPage- Navigate to a record detail pagestandard__objectPage- Navigate to an object's home/list viewstandard__webPage- Navigate to an external URLstandard__namedPage- Navigate to a named pagestandard__home- Navigate to the home page
Examples:
// Navigate to record page
this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: {
recordId: '001xx000003DGbQAAW',
actionName: 'view'
}
});
// Navigate to object home
this[NavigationMixin.Navigate]({
type: 'standard__objectPage',
attributes: {
objectApiName: 'Account',
actionName: 'home'
}
});
// Navigate to external URL
this[NavigationMixin.Navigate]({
type: 'standard__webPage',
attributes: {
url: 'https://example.com'
}
});
Navigation Best Practices
- Use
NavigationMixinfor all navigation within Salesforce - Use
GenerateUrlwhen you need the URL for external use (e.g., opening in new tab) - Handle navigation errors appropriately
- Provide user feedback for navigation actions
Change History
Version 1.0 - 2026-03-26
- Added initial version history tracking for this document.
Last Updated: 2026-03-26 Version: 1.0