---
name: sf-lwc-development
description: >-
LWC development — components, reactive properties, wire service, Apex integration, events, lifecycle hooks. Use when building LWC components or debugging wire/reactivity. Do NOT use for Aura, Visualforce, or Flow.
---
# LWC Development
Lightning Web Components (LWC) is Salesforce's modern component model based on web standards — Custom Elements, Shadow DOM, and ES modules.
## When to Use
- When building new Lightning Web Components from scratch
- When migrating Aura components to LWC
- When debugging reactive property or wire service issues
- When implementing parent-child communication with events or @api
- When adding LWC components to record pages, app pages, or Experience Cloud sites
@../_reference/LWC_PATTERNS.md
---
## Component Creation Procedure
Every LWC component is a folder with at minimum an HTML template and a JavaScript class.
```
force-app/main/default/lwc/
accountList/
accountList.html <- Template
accountList.js <- Component class
accountList.css <- Component-scoped styles (optional)
accountList.js-meta.xml <- Metadata (targets, properties)
```
### Step 1 — HTML Template
```html
{errorMessage}
{account.Name}
```
### Step 2 — JavaScript Class
```javascript
import { LightningElement, api, wire } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { NavigationMixin } from 'lightning/navigation';
import getAccounts from '@salesforce/apex/AccountsController.getAccounts';
export default class AccountList extends NavigationMixin(LightningElement) {
@api recordId;
@api maxRecords = 10;
accounts = [];
isLoading = false;
error;
get hasError() { return this.error !== undefined; }
get isEmpty() { return !this.isLoading && this.accounts.length === 0; }
get errorMessage() {
return this.error?.body?.message ?? this.error?.message ?? 'An unknown error occurred.';
}
connectedCallback() { this.loadAccounts(); }
handleViewAccount(event) {
this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: { recordId: event.currentTarget.dataset.id,
objectApiName: 'Account', actionName: 'view' }
});
}
async loadAccounts() {
this.isLoading = true;
this.error = undefined;
try {
this.accounts = await getAccounts({
searchTerm: this.searchTerm, maxRecords: this.maxRecords
});
} catch (error) {
this.error = error;
} finally {
this.isLoading = false;
}
}
}
```
### Step 3 — Meta XML
```xml
66.0
true
lightning__RecordPage
lightning__AppPage
lightning__HomePage
```
---
## Wire Service Usage
The wire service declaratively connects components to Salesforce data and re-runs when reactive parameters change.
### Wire with Apex
```javascript
import { LightningElement, wire, api } from 'lwc';
import { refreshApex } from '@salesforce/apex';
import getAccountDetails from '@salesforce/apex/AccountsController.getAccountDetails';
export default class AccountDetails extends LightningElement {
@api recordId;
_wiredResult;
@wire(getAccountDetails, { accountId: '$recordId' })
wiredAccount(result) {
this._wiredResult = result;
if (result.data) {
this.account = result.data;
this.error = undefined;
} else if (result.error) {
this.error = result.error;
this.account = undefined;
}
}
async handleSave(event) {
await updateAccount({ accountId: this.recordId, fields: event.detail.fields });
await refreshApex(this._wiredResult);
}
}
```
### Wire with Lightning Data Service
```javascript
import { LightningElement, api, wire } from 'lwc';
import { getRecord, getFieldValue, updateRecord } from 'lightning/uiRecordApi';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';
import ACCOUNT_INDUSTRY from '@salesforce/schema/Account.Industry';
export default class AccountHeader extends LightningElement {
@api recordId;
@wire(getRecord, { recordId: '$recordId', fields: [ACCOUNT_NAME, ACCOUNT_INDUSTRY] })
account;
get name() { return getFieldValue(this.account.data, ACCOUNT_NAME); }
get industry() { return getFieldValue(this.account.data, ACCOUNT_INDUSTRY); }
}
```
---
## Event Handling — Component Communication
### Child to Parent: Custom Events
```javascript
// child: opportunityCard.js
handleSelect() {
this.dispatchEvent(new CustomEvent('opportunityselect', {
detail: { opportunityId: this.opportunity.Id },
bubbles: false, composed: false
}));
}
```
```html
```
### Cross-Component: Lightning Message Service
```javascript
import { publish, subscribe, unsubscribe, MessageContext, APPLICATION_SCOPE }
from 'lightning/messageService';
import CHANNEL from '@salesforce/messageChannel/OpportunitySelected__c';
// Publisher
@wire(MessageContext) messageContext;
handleSelect(event) {
publish(this.messageContext, CHANNEL, { opportunityId: event.target.dataset.id });
}
// Subscriber
connectedCallback() {
this.subscription = subscribe(this.messageContext, CHANNEL,
(msg) => this.handleMessage(msg), { scope: APPLICATION_SCOPE });
}
disconnectedCallback() { unsubscribe(this.subscription); }
```
---
## Slots — Composition Patterns
```html
Edit Account
```
---
## Light DOM
Renders component markup directly into the parent DOM (no shadow boundary).
```javascript
export default class ThemedComponent extends LightningElement {
static renderMode = 'light';
}
```
Use for: global CSS theming, third-party library integration, Experience Cloud sites, simple leaf components. Query with `this.querySelector()` instead of `this.template.querySelector()`.
---
## Spring '26 Features
### SLDS 2.0
Use `--slds-c-*` styling hooks (replaces `--lwc-*` design tokens). Run `npx slds-lint` to check compliance.
### TypeScript Support
```bash
npm install --save-dev @salesforce/lightning-types
```
### Complex Template Expressions (Beta)
```html
{account.Name ?? 'Unknown Account'}
{formatRevenue(account.AnnualRevenue)}
```
Use getters for production code until this reaches GA.
### LWC in Screen Flows
Expose components as Flow screen actions via `lightning__FlowScreen` target. Implement `@api validate()` for Flow navigation validation.
---
## Related
### Guardrails
- **sf-lwc-constraints** — Enforced rules for LWC naming, reactivity, security, and accessibility
### Agents
- **sf-lwc-agent** — For interactive, in-depth LWC review guidance