SfSkills Domains Docs GitHub

Home / lwc / lwc-quick-actions

lwc-quick-actions

Use when building a Lightning Web Component that runs from a record page quick-action button — either a screen action that renders UI in a modal or a headless action that invokes logic with no UI. Triggers: 'lwc quick action on record page', 'headless quick action no ui', 'closeactionscreenevent not working', 'how to pass recordid into quick action lwc'. NOT for Flow screen components — use lwc/lwc-in-flow-screens. NOT for global actions without a record context — use admin/global-actions-and-quick-actions.

Open the skill folder on GitHub


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:


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

SituationRecommended ApproachReason
Simple confirm plus single-field updateHeadless action with invoke()No UI needed; platform closes on return
Multi-step UI, conditional branching, or formScreen actionModal template is the only place to render the form
Declarative owners, screen with validation, low codeFlow launched as a quick actionFlow Builder owns the UX without custom JS
Action needs to land the user on a new related recordScreen action with NavigationMixin before closeNavigation must fire before the modal is dispatched closed
Bulk action across a list viewList-view action wrapper (not a single-record action)recordId is not injected on list actions — design for a list of ids

  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


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

ArtifactDescription
Quick-action LWC bundleJS, HTML (if screen), and .js-meta.xml with the correct target and actionType
Post-action UX planNotes on toast copy, refresh strategy, and navigation target
Checker reportFindings on template-in-headless, missing @api recordId, premature close, and window.location usage

Package files