LearnFrenzy
Sign in Take a free test

Lightning Data Service · Lesson 14 of 25

LWC @wire and getRecord: Read Salesforce Records

5 min read Updated Nov 8, 2026 Saurabh Samir

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
  • getRecord with schema imports
  • getFieldValue and display values
  • Reactive parameters, errors and choosing LDS vs Apex

How @wire works

The wire service

  1. Declare@wire(getRecord, { recordId: '$recordId', fields }) on a field or function
  2. ProvisionLDS returns cached data immediately if it has it, otherwise loads it from the server
  3. Deliveryour field receives { data, error }, and the component re-renders
  4. 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';
    }
}

Result with the data LDS delivers

▌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: when recordId changes, LDS loads the new record automatically.
  • getFieldValue returns the raw value (including relationship fields like Owner.Name); getFieldDisplayValue returns 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

Result when LDS returns an error

▌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?

NeedUse
Fields of one or a few known recordsgetRecord / getRecords (LDS)
A ready-made view or edit formlightning-record-form and friends (next lesson)
Picklist values, object and field metadatagetPicklistValues, getObjectInfo (LDS)
Related lists, list viewsgetRelatedListRecords, getListRecordsByName (LDS)
Queries across objects, filters, aggregatesthe GraphQL wire adapter, or Apex
Complex business logic, callouts, many records at onceApex

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

  1. 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);
        }
    }

    Result

    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.

Next lesson

Popular