Lightning Data Service · Lesson 14 of 25
LWC @wire and getRecord: Read Salesforce Records
The most common job of a component is to show data from the record the user is looking at. Lightning Data Service (LDS) does that without a single line of Apex: you declare which fields you need with @wire(getRecord), and LDS loads them, respects the user's field-level security and sharing, caches them, and – crucially – keeps every component on the page in sync when the record changes.
In this lesson
- How the wire service works
getRecordwith schema importsgetFieldValueand display values- Reactive parameters, errors and choosing LDS vs Apex
How @wire works
The wire service
- Declare@wire(getRecord, { recordId: '$recordId', fields }) on a field or function
- ProvisionLDS returns cached data immediately if it has it, otherwise loads it from the server
- Deliveryour field receives { data, error }, and the component re-renders
- Stay in syncwhen the record changes anywhere through LDS, new data is delivered again
Reading fields from the current record
<!-- accountSummary.html -->
<template>
<lightning-card title="Account summary">
<template lwc:if={account.data}>
<div class="slds-p-horizontal_small">
<p><strong>{name}</strong> ({industry})</p>
<p>Annual revenue: {revenue}</p>
<p>Owner: {ownerName}</p>
<p>{tierLabel}</p>
</div>
</template>
<template lwc:elseif={account.error}>
<p class="slds-text-color_error">Couldn't load the account: {errorMessage}</p>
</template>
<template lwc:else>
<lightning-spinner alternative-text="Loading"></lightning-spinner>
</template>
</lightning-card>
</template>
// accountSummary.js
import { LightningElement, api, wire } from 'lwc';
import { getRecord, getFieldValue, getFieldDisplayValue } from 'lightning/uiRecordApi';
import NAME from '@salesforce/schema/Account.Name';
import INDUSTRY from '@salesforce/schema/Account.Industry';
import REVENUE from '@salesforce/schema/Account.AnnualRevenue';
import OWNER_NAME from '@salesforce/schema/Account.Owner.Name';
const FIELDS = [NAME, INDUSTRY, REVENUE, OWNER_NAME];
export default class AccountSummary extends LightningElement {
@api recordId; // set by the record page
@wire(getRecord, { recordId: '$recordId', fields: FIELDS })
account; // { data, error }
get name() {
return getFieldValue(this.account.data, NAME);
}
get industry() {
return getFieldValue(this.account.data, INDUSTRY) ?? 'No industry';
}
get revenue() {
return getFieldDisplayValue(this.account.data, REVENUE); // formatted in the user's locale and currency
}
get ownerName() {
return getFieldValue(this.account.data, OWNER_NAME);
}
get tierLabel() {
return getFieldValue(this.account.data, REVENUE) >= 10000000 ? '★ Strategic account' : 'Standard account';
}
get errorMessage() {
return this.account.error?.body?.message ?? 'Unknown error';
}
}
▌Account summary Acme Corporation (Manufacturing) Annual revenue: $12,500,000 Owner: Asha Verma ★ Strategic account
- Schema imports (
@salesforce/schema/Account.Name) are checked when you deploy: rename or delete the field and the deploy fails, and Salesforce won't let an admin delete a field your component references. That's far safer than strings like'Account.Name'. '$recordId'with a dollar sign makes the parameter reactive: whenrecordIdchanges, LDS loads the new record automatically.getFieldValuereturns the raw value (including relationship fields likeOwner.Name);getFieldDisplayValuereturns the formatted value for currency, dates and picklists.- The wired property starts as
{ data: undefined, error: undefined }, so the template shows the spinner until data arrives.
Handling errors
If the record doesn't exist or the user can't see it, the wire delivers an error instead:
// accountSummary.js – same component as above
▌Account summary Couldn't load the account: The requested resource does not exist
Wiring to a function
When you need to transform the data as it arrives, wire a function instead of a property:
@wire(getRecord, { recordId: '$recordId', fields: FIELDS })
wiredAccount({ data, error }) {
if (data) {
this.summary = `${getFieldValue(data, NAME)} – ${getFieldValue(data, INDUSTRY)}`;
this.error = undefined;
} else if (error) {
this.error = error;
this.summary = undefined;
}
}
LDS or Apex?
| Need | Use |
|---|---|
| Fields of one or a few known records | getRecord / getRecords (LDS) |
| A ready-made view or edit form | lightning-record-form and friends (next lesson) |
| Picklist values, object and field metadata | getPicklistValues, getObjectInfo (LDS) |
| Related lists, list views | getRelatedListRecords, getListRecordsByName (LDS) |
| Queries across objects, filters, aggregates | the GraphQL wire adapter, or Apex |
| Complex business logic, callouts, many records at once | Apex |
Prefer LDS when it can do the job
LDS gives you caching, security enforcement, offline support in the mobile app and automatic sync with other components – for free. Apex data is not synced: if another component updates the record, your Apex-loaded data stays stale until you refresh it.
Practice
Try it yourself
Show a contact's name and email from the current record, using getRecord and schema imports.
Show solution
<!-- contactHeader.html --> <template> <template lwc:if={contact.data}> <p>{name} · {email}</p> </template> </template>// contactHeader.js import { LightningElement, api, wire } from 'lwc'; import { getRecord, getFieldValue } from 'lightning/uiRecordApi'; import NAME from '@salesforce/schema/Contact.Name'; import EMAIL from '@salesforce/schema/Contact.Email'; export default class ContactHeader extends LightningElement { @api recordId; @wire(getRecord, { recordId: '$recordId', fields: [NAME, EMAIL] }) contact; get name() { return getFieldValue(this.contact.data, NAME); } get email() { return getFieldValue(this.contact.data, EMAIL); } }Ravi Kumar · ravi@example.com
Quick check
What does the $ in '$recordId' do?
Makes the parameter reactive: the wire re-runs when recordId changes.
Why import fields from @salesforce/schema?
Deploy-time validation and protection against deleting fields the component uses.
What's the shape of a wired property?
{ data, error } – both undefined until a result arrives.
Summary
- @wire(getRecord) loads fields with security, caching and automatic sync.
- Use schema imports, reactive '$' parameters, getFieldValue and getFieldDisplayValue.
- Handle data, error and loading states; prefer LDS over Apex when it can do the job.
Finished this lesson?
Mark it complete to track your progress, then carry on with the next lesson.