---
name: "Lightning Web Components"
description: "Always-on rules for writing Salesforce Lightning Web Components, their templates, styles, metadata and Jest tests."
applyTo: "**/lwc/**"
---

# Lightning Web Components rules

Get data through the platform's cached services first and write Apex last; every Apex method is code to secure, test and maintain.

## Data access

- Use base record forms (`lightning-record-form`, `-view-form`, `-edit-form`) for single-record view and edit. No Apex, FLS enforced, cached.
- Read record fields with `@wire(getRecord)` and `getFieldValue`; write with `createRecord`/`updateRecord`/`deleteRecord` from `lightning/uiRecordApi`.
- Use `getObjectInfo`, `getPicklistValues`, related list and GraphQL adapters before custom Apex. They respect record types and layouts.
- Use `@wire` for reads that should stay in sync; wired Apex must be `@AuraEnabled(cacheable=true)` and do no DML.
- Call Apex imperatively for mutations and user-triggered reads; debounce search input. One call per keystroke burns limits.
- After an imperative mutation, call `refreshApex(this.wiredResult)` or `notifyRecordUpdateAvailable` so other components update.
- Guard wire handlers against `undefined` data on first render; reactive params use `$recordId`.
- Prefer several small cacheable wires over one call returning the whole page; paginate server-side, never ship thousands of rows.

## Reactivity and lifecycle

- Reassign arrays and objects (`this.items = [...this.items, item]`) instead of mutating them; in-place mutation does not re-render.
- Use getters for derived values; do not store computed state.
- Use `@track` only for deep mutation you cannot replace with reassignment.
- Set up subscriptions in `connectedCallback` and remove them in `disconnectedCallback`. Leaks otherwise.
- Never fetch data in `renderedCallback`, and guard any work there with a flag. It runs on every render.
- Use `lwc:if`/`lwc:elseif`/`lwc:else` (not `if:true`), `key` on every `for:each` item, and `lwc:ref` for element access.

## Events and communication

- Parent to child through `@api` properties and methods; child to parent through `CustomEvent` with data in `detail`.
- Name events lowercase with no `on` prefix or hyphens; keep `bubbles` and `composed` false unless crossing a boundary is required.
- Use Lightning Message Service with a `MessageChannel` for unrelated components; no pub/sub helpers or `window` globals.
- Surface errors with `ShowToastEvent` or an inline message near the action; flatten `error.body` into readable text.

## Styling and accessibility

- Use Lightning Base Components before custom markup. They are accessible and themed.
- Customise with SLDS styling hooks (`--slds-g-*`, `--slds-c-*`); never reach into base component internals.
- Keep CSS scoped to the component with `:host`; no hard-coded colours where a token exists.
- Use Custom Labels (`@salesforce/label/c.X`) for all user-facing text. Needed for translation.
- Give icon-only buttons `alternative-text` and every input a `label` (use `variant="label-hidden"` to hide it visually).
- Add `role`, `aria-*`, focus handling and keyboard support to any raw interactive HTML.

## Security and runtime

- Respect Lightning Web Security/Locker: no `eval`, no `innerHTML` with user data, no reaching into other components' DOM.
- Load third-party libraries from static resources with `loadScript`/`loadStyle`, never from a CDN at runtime.
- Treat hiding fields in the UI as UX, not security; Apex must enforce access with `USER_MODE` and `with sharing`.
- Never put secrets in JavaScript or static resources; add external URLs to CSP Trusted Sites.
- Declare `targets` and typed `targetConfigs` with defaults in `js-meta.xml` so admins can configure components.

## Tests

- Write Jest tests with `@salesforce/sfdx-lwc-jest` in `__tests__/<component>.test.js`, one behaviour per `it`.
- Mock Apex with `jest.mock('@salesforce/apex/...', () => ({ default: jest.fn() }), { virtual: true })`; drive wires with `.emit(data)`/`.error(err)` and imperative calls with `mockResolvedValue`/`mockRejectedValue`.
- Emit LDS adapters (`getRecord.emit(fixture)`) with JSON fixtures in `__tests__/data/`.
- `await` a promise flush after every emit or action, and remove elements from `document.body` in `afterEach`. Prevents flaky, leaking tests.
- Assert rendered output, dispatched events, Apex parameters and error branches, and run `toBeAccessible()` from sa11y.

Go deeper: for larger tasks use the lwc-development, salesforce-frontend-architecture and salesforce-frontend-testing skills, and frontend-code-review before opening a pull request.
