Imperative vs Declarative Apex Method Calls in LWC
Understand the key differences between Imperative and Declarative Apex method calls in LWC — and know exactly when to use each approach.
1. Introduction
Apex communication is essential for Lightning Web Components (LWC) because it bridges the gap between the client-side (your component's JavaScript) and the server-side (your Salesforce org's data and logic).
In LWC, there are two ways to call server-side Apex methods:
• Imperative — Manually call the method when needed (e.g., on button click).
• Declarative — Automatically fetch data when the component loads using @wire.
This is a crucial concept for efficient and performant data handling in your LWC components.
2. Imperative (Manual Call)
Calls the Apex method on demand (e.g., when a button is clicked). Fetch data only when needed (e.g., user-triggered actions).
import { LightningElement } from 'lwc';
import getAccount from '@salesforce/apex/AccountController.getAccount';
export default class ImperativeExample extends LightningElement {
accountId = '001xx000003DGb0';
// Imperative call (triggered by a button click)
handleClick() {
getAccount({ accountId: this.accountId })
.then(data => {
console.log('Account Data:', data);
})
.catch(error => {
console.error('Error:', error);
});
}
}
<template>
<button onclick={handleClick}>Load Account</button>
</template>
• The Apex method is called only when the button is clicked — not on component load.
• Uses .then() to handle the successful result and .catch() to handle errors.
• Gives you full control over when and how the data is fetched.
3. Declarative (Using @wire)
Automatically fetches data when the component loads (like a subscription). Fetch data immediately when the component renders (e.g., load a record on page load).
import { LightningElement, wire } from 'lwc';
import getAccount from '@salesforce/apex/AccountController.getAccount';
export default class DeclarativeExample extends LightningElement {
// Declarative call using @wire
@wire(getAccount, { accountId: '001xx000003DGb0' })
wiredAccount({ data, error }) {
if (data) {
console.log('Account Data:', data);
} else if (error) {
console.error('Error:', error);
}
}
}
How it works: The @wire decorator automatically calls the Apex method getAccount when the component loads.
If the accountId changes, the method re-runs automatically.
4. Key Differences
| Aspect | Imperative | Declarative |
|---|---|---|
| Timing | Manually Triggered (e.g., button click) | Automatically on component load |
| Data Reactivity | Requires Manual Refresh | Updates if parameters change |
| Use Case | User-Triggered Actions | Initial data loading |
| Error Handling | Uses .then() and .catch() | Uses { data, error } in the wire method |
5. When to Use Which?
Use Declarative (@wire) when:
• You need a straightforward way to load data when the component is initialized.
• You want to leverage automatic updates to your component when the data changes.
• Lightning Data Service (LDS) handles caching and sharing data between components, which can boost performance.
Use Imperative when:
• The data retrieval depends on user interactions (e.g., button clicks, form submissions).
• You need to handle complex business logic before calling the Apex method.
• You need to perform DML operations (insert, update, delete) — @wire only supports read-only operations.
• When caching is not beneficial or you need fresh data every time.
6. Quick Reference
Imperative syntax:
// Call on button click or any event
apexMethodName({ param1: value1 })
.then(result => { /* handle result */ })
.catch(error => { /* handle error */ });
Declarative syntax (wire as property):
@wire(apexMethodName, { param1: value1 })
propertyName;
Declarative syntax (wire as function):
@wire(apexMethodName, { param1: '$reactiveParam' })
wiredMethodName({ data, error }) {
if (data) { /* handle data */ }
else if (error) { /* handle error */ }
}
📝 Remember: Use cacheable=true on your Apex method annotation when using @wire.
For imperative calls that perform DML, do NOT use cacheable=true.
