LWC Wire Refresh Patterns
Activate when an LWC's @wire-provisioned data becomes stale after an imperative DML, child record change, or cross-component event. Salesforce offers several refresh primitives — refreshApex, getRecordNotifyChange (deprecated), the newer RefreshView API, and wire re-provisioning via parameter change — each fits a specific scenario.
Before Starting
- Identify the wire adapter. Custom Apex wire? Use
refreshApex. StandardgetRecord? UseRefreshView(or legacynotifyRecordUpdateAvailable). - Know about RefreshView. The
lightning/refreshmodule shipped in Spring '23 (beta at launch, GA thereafter). It replaces Aura'sforce:refreshViewand is the view-scoped alternative togetRecordNotifyChangefor standard UI API refreshes. - Avoid forced re-render hacks like nulling params then restoring — tends to break and confuses framework caching.
Core Concepts
refreshApex(wiredValue)
For custom Apex wires. Hold onto the raw wired value (not the destructured data) and call refreshApex(this._wiredAccounts):
@wire(getAccounts, { filter: '$filter' })
wiredAccounts;
handleRefresh() { return refreshApex(this.wiredAccounts); }
RefreshView API
Refresh signal across the page/view. The publisher side is an event dispatch:
import { RefreshEvent } from 'lightning/refresh';
this.dispatchEvent(new RefreshEvent());
The subscriber side is imperative registration, not a wire. RefreshView is not a wire adapter and cannot be used with @wire. The lightning/refresh module exports four functions plus the event: registerRefreshContainer(), registerRefreshHandler(), unregisterRefreshContainer(), unregisterRefreshHandler(), and RefreshEvent (plus the REFRESH_COMPLETE, REFRESH_COMPLETE_WITH_ERRORS, REFRESH_ERROR status constants).
A leaf component participates by registering a handler in connectedCallback() and tearing it down in disconnectedCallback():
import { LightningElement } from 'lwc';
import { registerRefreshHandler, unregisterRefreshHandler } from 'lightning/refresh';
export default class RefreshHandler extends LightningElement {
refreshHandlerID;
connectedCallback() {
this.refreshHandlerID = registerRefreshHandler(this, this.refreshHandler);
}
disconnectedCallback() {
unregisterRefreshHandler(this.refreshHandlerID);
}
refreshHandler() {
// must return a Promise so the container can await completion
return refreshApex(this.wiredResult);
}
}
A container component uses registerRefreshContainer(this, this.refreshContainer) and receives a promise it can inspect against REFRESH_COMPLETE / REFRESH_COMPLETE_WITH_ERRORS / REFRESH_ERROR. Registered handlers are invoked from the container node outward. Note that registerRefreshHandler returns an ID, and it is that ID — not the component — that you pass to unregisterRefreshHandler.
notifyRecordUpdateAvailable (legacy)
import { notifyRecordUpdateAvailable } from 'lightning/uiRecordApi'; — still works; informs LDS that specific records changed.
Wire re-provision by param change
Changing a reactive @wire parameter re-runs the wire automatically. Useful when refresh is triggered by user action that changes context.
Common Patterns
Pattern: refreshApex after imperative DML
async save() {
await updateAccount({ acc: this.acc });
await refreshApex(this.wiredAccounts);
}
Pattern: Dispatch RefreshEvent from a child after save
Child modal saves a record → dispatches new RefreshEvent() → parent (or the view) refreshes its wires.
Pattern: Cross-component refresh via Lightning Message Service
Sibling components can't share wired data. Use LMS to publish a "data changed" event; siblings subscribe and call their own refresh.
Decision Guidance
| Scenario | Refresh mechanism |
|---|---|
| Custom Apex wire, same component | refreshApex(wiredValue) |
| Standard getRecord, after save | RefreshView (RefreshEvent) or notifyRecordUpdateAvailable |
| Cross-view, global refresh | RefreshEvent at the app-level listener |
| Sibling components needing refresh | Lightning Message Service + per-component refresh |
| Param-driven context change | Reassign reactive @wire param |
Recommended Workflow
- Identify the wire adapter — custom Apex vs UI API.
- For custom Apex: store the raw wired value; call
refreshApexon it. - For UI API: dispatch
RefreshEventor callnotifyRecordUpdateAvailable. - For sibling coordination: wire LMS; subscribe and refresh per-component.
- Avoid param-nulling hacks.
- Test refresh flows with Jest (mock
refreshApex+ wire adapters). - Document refresh ownership — which component is responsible for triggering refresh.
Review Checklist
- Custom Apex wires use
refreshApex(rawWiredValue) - Standard wires use
RefreshEventornotifyRecordUpdateAvailable - No param-null-then-restore hacks
- Cross-component refresh coordinated via LMS or parent
- Refresh triggers after imperative DML, not before
- Jest tests cover refresh flow
- Migration plan from deprecated
getRecordNotifyChangedocumented
Salesforce-Specific Gotchas
refreshApexrequires the RAW wired value, not the destructureddata. StorewiredFoo(full object) notwiredFoo.data.getRecordNotifyChangeis deprecated. Migrate toRefreshView+notifyRecordUpdateAvailable.- Reassigning
@trackdata to itself does NOT refresh wires. Wires re-run only when reactive params change or explicit refresh is invoked.
Output Artifacts
| Artifact | Description |
|---|---|
| Refresh pattern selection | Per wire / per trigger mapping |
| LMS channel for refresh signals | Cross-component plumbing |
| Migration plan | Legacy getRecordNotifyChange → RefreshView |
Related Skills
lwc/lwc-wire-service— wire fundamentalslwc/lwc-lightning-message-service— cross-component eventslwc/lwc-lds-writes— updateRecord/createRecord semantics