# Lwc Quick Actions

> Builds Lightning Web Components for record-page quick actions, covering screen actions with modals and headless actions with no UI, including lifecycle, recordId injection, and post-action UX.

- Skill: `pranavnagrecha/lwc-quick-actions` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds add pranavnagrecha/lwc-quick-actions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pranavnagrecha/lwc-quick-actions/raw
- Safety review: CAUTION (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend, Coding & Dev Tools, Frontend Frameworks
- Tags: Apex, Javascript, Lightning Web Components, Lwc, Quick Actions, Record Page, Salesforce
- Author: PranavNagrecha (https://skillmd.com/u/pranavnagrecha)
- Updated: 2026-08-22
- Page: https://skillmd.com/skills/pranavnagrecha/lwc-quick-actions

---


# LWC Quick Actions

Use this skill when a record-page button needs to launch a Lightning Web Component — either as a modal (screen action) or as silent on-click logic (headless action). The right choice depends on whether the user has to see or confirm anything, not on what is easier to code.

---

## Before Starting

Gather this context first:

- What object does this action run on, and what fields does it read or write?
- Does the user need to see or confirm anything, or is this a one-tap operation?
- After success, should the page toast, refresh the record, navigate, or stay where it is?
- Is the called Apex `@AuraEnabled (with sharing)` and does it still enforce CRUD/FLS?
- Is this invoked from a record page, a list view, or a related list — the injected inputs differ.

---

## Core Concepts

Quick-action LWCs are regular components with a specific meta.xml shape and a specific lifecycle contract. Two modes exist: screen actions (render UI in a modal) and headless actions (no UI, a single `invoke()` method).

### Declaration In `.js-meta.xml`

The bundle must declare the `lightning__RecordAction` target and, inside `<targetConfigs>`, set `actionType="ScreenAction"` for a modal or `actionType="Action"` for headless. Without that target config the component will not appear in the quick-action picker. The target also controls which inputs the platform injects — `lightning__RecordAction` is what causes `recordId` to flow in.

### `recordId` Is Auto-Injected As `@api`

When the target is `lightning__RecordAction`, Salesforce injects `recordId` into the component's `@api recordId` property before `connectedCallback`. If the property is not declared with `@api`, the value never lands. `objectApiName` is not auto-injected on quick actions — if you need it, pass it through the meta.xml config or look it up via `getRecord`.

### Screen Actions Close Through An Event

A screen action renders its template inside the quick-action modal. To dismiss the modal, the component imports `CloseActionScreenEvent` from `lightning/actions` and dispatches it: `this.dispatchEvent(new CloseActionScreenEvent())`. This event does not save anything — it is purely a close signal, so the save must complete before the dispatch, not after.

### Headless Actions Implement `invoke()`

A headless action has no template. Instead it exposes an `@api invoke()` method that Salesforce calls when the button is clicked. The platform dismisses the action when `invoke()` returns (or its returned promise resolves). Toasts fired via `ShowToastEvent` from within `invoke()` render on the host page — that is the supported way to give feedback from a headless action.

### Post-Action UX Must Be Deliberate

Screen actions do not automatically refresh the record. For an in-page refresh use `getRecordNotifyChange` from `lightning/uiRecordApi` or `refreshApex` on a wire. For a hard navigation use `NavigationMixin.Navigate` with `standard__recordPage` — do not reach for `window.location.reload()`, which breaks SPA navigation and destroys in-flight state.

### Security Still Applies

`@AuraEnabled` does not bypass CRUD/FLS. Use `WITH USER_MODE` in SOQL/DML or `Security.stripInaccessible` in the Apex called by the action. The quick-action surface does not change the permission model — the user runs the action as themselves.

---

## Common Patterns

### Screen Action — Confirm, Edit, Save, Close

**When to use:** The user needs to see current values, edit something, or read a warning before the save commits.

**How it works:** Declare `lightning__RecordAction` with `actionType="ScreenAction"`. Render UI bound to `@api recordId`. On submit, call imperative Apex or a `lightning-record-edit-form`, await the result, show a toast, and then dispatch `CloseActionScreenEvent`. Optionally call `getRecordNotifyChange([{ recordId }])` so the record page reflects the change.

**Why not the alternative:** A headless action cannot show the form. A Flow launched as a quick action is appropriate when the UI is declarative and low-code ownership is a goal, but it carries Flow-runtime overhead and a different debugging story.

### Headless Action — One-Tap Status Change

**When to use:** A single-field update, a confirmation toast, no form needed. Examples: "Mark as Read", "Approve", "Re-send Welcome Email".

**How it works:** Declare `actionType="Action"`. Omit the template. Implement `async invoke()` that calls the Apex method, awaits the result, fires `ShowToastEvent`, and returns. Do not dispatch `CloseActionScreenEvent` — there is no modal to close.

**Why not the alternative:** A screen action with an auto-submitting template flickers a modal for no reason. A Flow would be overkill for a single-field update.

### Screen Action With Post-Save Navigation

**When to use:** The action creates a related record and the user should land on it.

**How it works:** After the Apex save returns the new Id, call `NavigationMixin.Navigate({ type: 'standard__recordPage', attributes: { recordId: newId, objectApiName: 'Case', actionName: 'view' } })`, then dispatch `CloseActionScreenEvent`. The modal closes as the page transitions.

---

## Decision Guidance

| Situation | Recommended Approach | Reason |
|---|---|---|
| Simple confirm plus single-field update | Headless action with `invoke()` | No UI needed; platform closes on return |
| Multi-step UI, conditional branching, or form | Screen action | Modal template is the only place to render the form |
| Declarative owners, screen with validation, low code | Flow launched as a quick action | Flow Builder owns the UX without custom JS |
| Action needs to land the user on a new related record | Screen action with `NavigationMixin` before close | Navigation must fire before the modal is dispatched closed |
| Bulk action across a list view | List-view action wrapper (not a single-record action) | `recordId` is not injected on list actions — design for a list of ids |

---

## Recommended Workflow

1. Confirm the UX — modal or no modal? If no UI is needed, pick headless.
2. Write `.js-meta.xml` with `lightning__RecordAction` and the correct `actionType`. Add `<targetConfig>` entries only for targets you actually support.
3. Declare `@api recordId` (and `@api invoke` if headless). Read only the fields you need via `getRecord` or Apex.
4. For screen actions, do the save first, then fire the toast, then dispatch `CloseActionScreenEvent`. For headless, resolve from `invoke()` after the toast.
5. Decide the refresh strategy — `getRecordNotifyChange`, `refreshApex`, or `NavigationMixin`. Never use `window.location.reload()`.
6. Run `python3 scripts/check_lwc_quick_actions.py` over the bundle to catch headless-with-template, missing `@api recordId`, pre-save close, and `window.location` usage.
7. Add an Apex test for the called method and a Jest test for the JS — verify the close event fires only after the save resolves.

---

## Review Checklist

- [ ] `.js-meta.xml` declares `lightning__RecordAction` and a matching `<targetConfig>` with the correct `actionType`.
- [ ] `@api recordId` is declared in the JS — the component actually receives the record context.
- [ ] Screen actions save first and dispatch `CloseActionScreenEvent` only after the save resolves.
- [ ] Headless actions have no `<template>` and the action closes by returning from `invoke()`.
- [ ] Post-save refresh uses `getRecordNotifyChange`, `refreshApex`, or `NavigationMixin` — never `window.location.reload()`.
- [ ] The Apex called by the action enforces CRUD/FLS (`WITH USER_MODE` or `stripInaccessible`).
- [ ] Toasts use `ShowToastEvent` and fire before the close/return.

---

## Salesforce-Specific Gotchas

1. **`CloseActionScreenEvent` does not save** — it only dismisses the modal. Dispatching it before the Apex save resolves throws away the save because the component is destroyed mid-promise.
2. **Headless actions render nothing** — any `<template>` content in an `actionType="Action"` bundle is ignored; if you need a confirm dialog, use `LightningConfirm` from inside `invoke()` and still resolve the promise to close.
3. **`recordId` is only auto-injected under `lightning__RecordAction`** — switching the target (e.g., to `lightning__AppPage`) silently stops the injection and `recordId` becomes `undefined`.
4. **Modal size is not freely configurable** — quick-action modals follow the platform's sizing rules; a dedicated wide UI usually belongs in `lwc-lightning-modal` or a full page instead.
5. **List-view actions do not receive `recordId`** — they receive a list of record ids via the action target config. Reusing a single-record component on a list surface breaks silently.

---

## Output Artifacts

| Artifact | Description |
|---|---|
| Quick-action LWC bundle | JS, HTML (if screen), and `.js-meta.xml` with the correct target and `actionType` |
| Post-action UX plan | Notes on toast copy, refresh strategy, and navigation target |
| Checker report | Findings on template-in-headless, missing `@api recordId`, premature close, and `window.location` usage |

---

## Related Skills

- `lwc/lwc-lightning-modal` — use when the UI needs to be a modal invoked from outside a quick action, or when the quick-action modal sizing is insufficient.
- `lwc/lwc-imperative-apex` — use for the Apex-call patterns inside the action, including error handling and `@AuraEnabled(cacheable=true)` constraints.
- `lwc/navigation-and-routing` — use when deciding between `NavigationMixin`, `getRecordNotifyChange`, and `refreshApex` after the action completes.
- `lwc/lwc-in-flow-screens` — use when the component belongs inside a Flow screen rather than as a record-page quick action.

