---
description: Lightning Web Component development including component architecture, wire service, imperative Apex calls, events, navigation, and SLDS styling. Auto-invoked when working with .js, .html, .css files inside lwc/ directories.
---

# LWC Development

You are an expert Lightning Web Component developer. Build performant, accessible components following Salesforce platform standards.

## Component Architecture

### File Structure
```
myComponent/
├── myComponent.html          # Template
├── myComponent.js            # Controller
├── myComponent.css           # Styles (scoped)
├── myComponent.js-meta.xml   # Metadata config
└── __tests__/                # Jest tests
    └── myComponent.test.js
```

### Metadata Configuration
```xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>62.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
        <target>lightning__FlowScreen</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <objects>
                <object>Account</object>
            </objects>
            <property name="title" type="String" default="My Component"/>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>
```

## Data Patterns

### Wire Service (Reactive, Cached)
```javascript
import { LightningElement, wire, api } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';

export default class ContactList extends LightningElement {
    @api recordId;

    @wire(getContacts, { accountId: '$recordId' })
    contacts;

    get hasContacts() {
        return this.contacts?.data?.length > 0;
    }

    get error() {
        return this.contacts?.error?.body?.message;
    }
}
```

### Imperative Apex (On-Demand)
```javascript
import getContacts from '@salesforce/apex/ContactController.getContacts';

async handleRefresh() {
    try {
        this.contacts = await getContacts({ accountId: this.recordId });
    } catch (error) {
        this.dispatchEvent(new ShowToastEvent({
            title: 'Error',
            message: error.body?.message || 'Unknown error',
            variant: 'error'
        }));
    }
}
```

### When to Use Which
- **Wire**: Read-only data that should auto-refresh. Most common pattern.
- **Imperative**: User-triggered actions, DML operations, conditional data loading.

## Event Communication

### Child → Parent: Custom Events
```javascript
// Child fires
this.dispatchEvent(new CustomEvent('selected', {
    detail: { recordId: this.record.Id }
}));

// Parent handles
<c-child onselected={handleSelected}></c-child>
```

### Unrelated Components: Lightning Message Service
```javascript
import { publish, subscribe, MessageContext } from 'lightning/messageService';
import RECORD_SELECTED from '@salesforce/messageChannel/RecordSelected__c';

@wire(MessageContext) messageContext;

publishSelection(recordId) {
    publish(this.messageContext, RECORD_SELECTED, { recordId });
}
```

## Best Practices

### Performance
- Use `@wire` for data loading (leverages LDS cache)
- Avoid excessive DOM operations — let the framework handle reactivity
- Use `if:true` / `if:false` directives to conditionally render (not CSS `display:none`)
- Lazy-load child components with dynamic imports when possible
- Debounce search inputs: don't call Apex on every keystroke

### Security
- **Never use `innerHTML`** — use template directives instead
- Sanitize any user input before passing to Apex
- Handle FLS errors gracefully (wire errors when fields aren't accessible)
- Use `lightning/platformResourceLoader` for external scripts, not raw `<script>` tags

### Accessibility
- Use SLDS classes for styling (built-in accessibility)
- Add `aria-label` attributes to interactive elements
- Ensure keyboard navigation works
- Use semantic HTML elements
- Test with screen readers

### Testing
```javascript
// Jest test example
import { createElement } from 'lwc';
import MyComponent from 'c/myComponent';
import getContacts from '@salesforce/apex/ContactController.getContacts';

jest.mock('@salesforce/apex/ContactController.getContacts',
    () => ({ default: jest.fn() }),
    { virtual: true }
);

describe('c-my-component', () => {
    afterEach(() => { while (document.body.firstChild) document.body.removeChild(document.body.firstChild); });

    it('displays contacts', async () => {
        getContacts.mockResolvedValue([{ Id: '1', Name: 'Test' }]);
        const element = createElement('c-my-component', { is: MyComponent });
        element.recordId = '001xx000003DGbx';
        document.body.appendChild(element);
        await Promise.resolve();
        const items = element.shadowRoot.querySelectorAll('li');
        expect(items.length).toBe(1);
    });
});
```

## SF CLI Commands
```bash
# Create new LWC
sf lightning generate component -n myComponent -d force-app/main/default/lwc

# Run Jest tests
npm run test:unit
npx jest --coverage

# Deploy LWC
sf project deploy start -m "LightningComponentBundle:myComponent"
```

## When to Use This Skill
- Building new Lightning Web Components
- Converting Aura components to LWC
- Implementing wire service or imperative Apex patterns
- Designing component communication (events, LMS)
- Writing Jest tests for LWC
- Building components for Flow Screens

---

*Free Claude Code skill from [Cumulus Vision](https://cumulusvision.com) — Senior Salesforce engineering for complex environments.*
