The quick version: an LWC can be a quick action in two shapes. A screen action opens your component in a modal and you close it yourself with CloseActionScreenEvent. A headless action renders nothing and runs an @api invoke() method when the button is clicked. Both are declared in the component’s meta file with the lightning__RecordAction target, and both are object-specific: a global quick action cannot be an LWC.
Declaring it
The meta XML decides which shape you get.
<?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__RecordAction</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__RecordAction">
<actionType>ScreenAction</actionType>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
ScreenAction gives a modal. Action gives a headless action. Leave the targetConfig out entirely and you get the screen behaviour by default, which is fine but worth stating explicitly so the next person does not have to guess.
After deploying, the action still has to be created in Setup on the object, with type Lightning Web Component, and then added to the page layout or the Lightning record page’s highlights panel. Deploying the component alone puts nothing in front of a user.
Screen actions
Your component’s template renders inside a modal the platform provides. You do not write the header, the backdrop or the close button.
import { LightningElement, api } from 'lwc';
import { CloseActionScreenEvent } from 'lightning/actions';
export default class LogVisit extends LightningElement {
@api recordId;
@api objectApiName;
handleCancel() {
this.dispatchEvent(new CloseActionScreenEvent());
}
async handleSave() {
await createVisit({ accountId: this.recordId, notes: this.notes });
this.dispatchEvent(new CloseActionScreenEvent());
}
}
The action’s label comes from the quick action definition in Setup, not from your component. The modal sizes itself to your content, so put your own container and SLDS spacing around it rather than expecting a standard layout.
Nothing closes the modal automatically. If your save path forgets to dispatch CloseActionScreenEvent, the user is left staring at a form that already submitted, and they will click Save again. Dispatch it on cancel, on success, and after a navigation.
Be careful with the order when you both navigate and close. Fire the navigation first and the close second, otherwise the modal can persist over the destination page.
Headless actions
No template content is rendered. The platform calls invoke() each time the button is clicked.
<targetConfig targets="lightning__RecordAction">
<actionType>Action</actionType>
</targetConfig>
export default class MarkReviewed extends LightningElement {
@api recordId;
@api async invoke() {
try {
await markReviewed({ recordId: this.recordId });
this.dispatchEvent(new ShowToastEvent({
title: 'Marked as reviewed', variant: 'success' }));
getRecordNotifyChange([{ recordId: this.recordId }]);
} catch (error) {
this.dispatchEvent(new ShowToastEvent({
title: 'Could not update the record',
message: reduceErrors(error).join(', '),
variant: 'error', mode: 'sticky' }));
}
}
}
The method must be called invoke, must be decorated @api, and takes no arguments. The component’s HTML file should exist but render nothing.
Two rules that follow from there being no screen. Always tell the user something happened, because a button that appears to do nothing gets clicked repeatedly. And always refresh the record, since the page will not notice your update on its own; getRecordNotifyChange from lightning/uiRecordApi tells Lightning Data Service to revalidate.
Nothing stops the user clicking twice while the call is in flight. If the operation is not idempotent, guard it in Apex rather than in JavaScript.
Picking between them
Headless when the action takes no input and the outcome is obvious: mark as reviewed, generate a document, send for approval where the approver is already determined. The user gets one click instead of a modal they have to dismiss.
Screen when you need input, a confirmation, or a summary of what is about to happen. Also screen when the operation is destructive, because the modal is the confirmation step.
Before writing either, check whether a standard action already does it. Update a field on the record and a standard Update Record action configured in Setup needs no code at all.
Frequently asked
Can an LWC be a global quick action? No. LWC quick actions are object-specific record actions. For a global action, use a screen flow or an Aura component.
How do I control the modal width? You cannot set it directly for a quick action. Style your own container inside it.
Do quick actions work in the mobile app? Generally yes on record pages, and layout assumptions that hold on a desktop often do not. Test on a device.
Why is my action missing from the layout? The component must be exposed with the record action target, and the action must be added to the layout’s Salesforce Mobile and Lightning Experience Actions section.
