Skip to main content

Discover Lightning Web Component Actions

Learning Objectives

After completing this unit, you’ll be able to:

  • Create a Lightning web component and action to populate fields based on user input and provide feedback messages to users.
  • Start migrating your JavaScript custom button functionality to Lightning-friendly alternatives.

Lightning Web Component Actions: Smart, Fast, and Flexible

We've covered several solutions that work in both Lightning Experience and Salesforce Classic and that are great replacements for JavaScript buttons. But we recognize that those declarative and programmatic solutions don’t address every use case. The good news is that there’s something that addresses many more. Introducing Lightning web component actions: quick actions that call Lightning web components.

Note

As of the Spring ’19 release (API version 45.0), you can build Lightning components using two programming models: the Lightning Web Components model and the original Aura Components model. Lightning web components and Aura components can coexist and interoperate on a page. This content covers Lightning web components.

Lightning web component actions are built on the existing Lightning Web Component framework. You can easily convert your existing Lightning web components to actions and use them in Lightning Experience.

You make a Lightning web component invokable as an action by adding lightning__RecordAction as a target in the component’s metadata configuration XML file and specifying the actionTypefor the targetConfigs. For the action to appear in a modal window defined by Salesforce, you set the actionType to ScreenAction. To execute the action without a modal, or to define a custom modal window, you set the actionType to Action.

Another useful property for a Lightning web component action is recordId, which provides the record context to the component when it’s invoked from a Lightning record page. To access a record’s data or metadata, in the component’s JavaScript class, you use the @api decorator to create a public recordId property.

Note

If you haven’t built Lightning web components before, visit the Lightning Web Components (LWC) Development Center, do the Lightning Web Components Quick Start project, and complete the Lightning Web Components Basics module.

Let’s dive into Lightning web component actions. Next, we talk about some JavaScript button functionality that can be addressed with Lightning web component actions instead, like populating fields based on user input and providing feedback messages during data entry.

Populate Fields Based on User Input and Provide Feedback Messages to Users

Let’s say you use JavaScript buttons to validate or manipulate data and give your users feedback or instructions when they work with records. Here’s an example to show how easily you can validate or manipulate data in a Lightning web component before your users create or update a record.

We built a custom object in our sample org called Case Study, which we use to track different research projects. We also have a Test User custom object so that we can add new test users to case studies and capture their names and email addresses. Since the email address is our primary contact method, we want to make sure it’s entered correctly. We also want to create a unique nickname for test users in the case study so that they can remain anonymous.

We’re going to create a Lightning web component action that displays the name and email address fields, validates the email address, and uses the first name and a random number to automatically create the nickname.

Here’s the code for the component called by this Lightning web component action.

  • createUser.html: The structure for the Lightning web component that you see when you open the action. It contains the UI implementation, which includes the text fields, buttons, action title, and so on.
  • createUser.js: The controller that listens to the component and any UI events that take place, such as the render lifecycle, text input, and button clicks. JavaScript methods define what to do with input, data, events, changes to state, and more to make your component work.
  • createUser.js-meta.xml: The code that defines the metadata for the component, including the design configuration for components intended for use in Lightning Experience.

createUser.html

<template>
 <lightning-quick-action-panel header="Add Test User to Case Study">
     <lightning-input
             type="text"
             data-field="firstName"
             label="User's First Name"
             onblur={updateNickName}
             required
     ></lightning-input>
     <lightning-input
             type="text"
             data-field="lastName"
             label="User's Last Name"
             required
     ></lightning-input>
     <lightning-input
             type="text"
             data-field="nickName"
             label="Nickname"
             required
     ></lightning-input>
     <lightning-input
             type="email"
             data-field="email"
             label="User's Email"
             onblur={handleEmailBlur}
             required
     ></lightning-input>
     <lightning-input
             type="password"
             data-field="password"
             label="User's Password"
             onblur={handlePasswordBlur}
             required
     ></lightning-input>
     <div slot="footer">
         <lightning-button
                 variant="neutral"
                 label="Cancel"
                 onclick={handleCancel}
         ></lightning-button>
         <lightning-button
                 variant="brand"
                 label="Save User"
                 onclick={handleSave}
         ></lightning-button>
     </div>
 </lightning-quick-action-panel>
</template>

createUser.js

import { LightningElement, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { CloseActionScreenEvent } from 'lightning/actions';
import { createRecord } from 'lightning/uiRecordApi';
import TEST_USER_OBJECT from '@salesforce/schema/Test_User__c';
import NAME_FIELD from '@salesforce/schema/Test_User__c.Name';
import PASSWORD_FIELD from '@salesforce/schema/Test_User__c.Password__c';
import EMAIL_FIELD from '@salesforce/schema/Test_User__c.Email__c';
import NICKNAME_FIELD from '@salesforce/schema/Test_User__c.Nickname__c';
import CASE_STUDY_FIELD from '@salesforce/schema/Test_User__c.Case_Study__c';
export default class CreateUser extends LightningElement {
   @api recordId;
   async handleSave() {
       // query form elements
       const firstNameEl = this.template.querySelector('[data-field="firstName"]');
       const lastNameEl = this.template.querySelector('[data-field="lastName"]');
       const nickNameEl = this.template.querySelector('[data-field="nickName"]');
       const emailEl = this.template.querySelector('[data-field="email"]');
       const passwordEl = this.template.querySelector('[data-field="password"]');
       // check for errors on form elements (custom or otherwise)
       const isFirstNameValid = firstNameEl.reportValidity();
       const isLastNameValid = lastNameEl.reportValidity();
       const isNickNameValid = nickNameEl.reportValidity();
       const isPasswordValid = passwordEl.reportValidity();
       const isEmailValid = emailEl.reportValidity();
       if(!isFirstNameValid || !isLastNameValid || !isNickNameValid || !isPasswordValid || !isEmailValid) {
           return;
       }
       const recordInput = {
           apiName: TEST_USER_OBJECT.objectApiName,
           fields: {
               [NAME_FIELD.fieldApiName]: `${firstNameEl.value} ${lastNameEl.value}`,
               [PASSWORD_FIELD.fieldApiName]: passwordEl.value,
               [EMAIL_FIELD.fieldApiName]: emailEl.value,
               [NICKNAME_FIELD.fieldApiName]: nickNameEl.value,
               [CASE_STUDY_FIELD.fieldApiName]: this.recordId,
           }
       };
       try {
           await createRecord(recordInput);
           this.dispatchEvent(
               new ShowToastEvent({
                   title: 'Success!',
                   message: 'The test user has been created.',
                   variant: 'success'
               })
           );
           this.dispatchEvent(new CloseActionScreenEvent());
       } catch (error) {
           new ShowToastEvent({
               title: 'Error creating the test user, try again...',
               message: error.body.message,
               variant: 'error'
           });
       }
   }
   updateNickName() {
       const firstName = this.template.querySelector('[data-field="firstName"]').value;
       const nickNameEl = this.template.querySelector('[data-field="nickName"]');
       if(firstName && !nickNameEl.value) {
           const today = new Date();
           nickNameEl.value = firstName + today.valueOf(today)
       }
   }
   handleCancel() {
       this.dispatchEvent(new CloseActionScreenEvent());
   }
   handlePasswordBlur() {
       const passwordEl = this.template.querySelector('[data-field="password"]');
       // clear custom errors
       passwordEl.setCustomValidity('');
       this.validatePassword(passwordEl);
   }
   validatePassword(input) {
       // check lightning-input validity
       if(input.reportValidity()) {
           // perform custom validation
           const value = input.value;
           if (value === undefined) {
               input.setCustomValidity('You must enter a password.');
           } else if (value.length < 7) {
               input.setCustomValidity('The password is the wrong length (must be >= 7): ' + value.length);
           } else if (value.length > 15) {
               input.setCustomValidity('The password is the wrong length (must be <= 15): ' + value.length);
           } else if (value.search(/[0-9]+/) === -1) {
               input.setCustomValidity('The password must contain at least one number.');
           } else if (value.search(/[a-zA-Z]+/) === -1) {
               input.setCustomValidity('The password must contain at least one letter.');
           }
           // display custom validation errors (if any)
           input.reportValidity();
       }
   }
   handleEmailBlur() {
       const emailEl = this.template.querySelector('[data-field="email"]');
       // clear Custom Errors
       emailEl.setCustomValidity('');
       this.validateEmail(emailEl);
   }
   validateEmail(input) {
       // check lightning-input validity
       if (input.reportValidity()) {
           const value = input.value;
           if (!/@gmail\.com$/.test(value)) {
               input.setCustomValidity('Email must be a Gmail account.');
           }
           // display custom validation errors (if any)
           input.reportValidity();
       }
   }
}

createUser.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
   <apiVersion>57.0</apiVersion>
   <isExposed>true</isExposed>
   <targets>
       <target>lightning__RecordAction</target>
   </targets>
   <targetConfigs>
       <targetConfig targets="lightning__RecordAction">
           <actionType>ScreenAction</actionType>
       </targetConfig>
   </targetConfigs>
</LightningComponentBundle>

Create the Lightning Web Component Action

After creating the Lightning web component, we assign it to an action. In the object management settings for Case Study, we go to Buttons, Links, and Actions, click New Action, then configure the action with these parameters.

Field Value

Action Type

Lightning Web Component

Lightning Web Component

c:createUser

Label

Create Test User

Name

Create_Test_User

Then we add our new Lightning web component action to the Case Study page layout. When users invoke it from a case study record detail page, they see the Lightning web component action that we created.

Add User LWC action in Lightning Experience.

Note

Lightning web component actions aren’t available on the Salesforce mobile app. To create an action that’s available on the Salesforce mobile app, use the Aura programming model to create a Lightning component action.

Lightning web component actions are the future of programmatic actions in Lightning Experience. We hope that you start looking at this set of solutions as a better alternative to JavaScript buttons.

And if you depend on partner apps that use JavaScript buttons, you’ll be glad to know that many of our partners have already started migrating and upgrading their apps to Lightning. You can find more apps on the AppExchange that are updated for Lightning Experience.

Resources

Keep learning for
free!
Sign up for an account to continue.
What’s in it for you?
  • Get personalized recommendations for your career goals
  • Practice your skills with hands-on challenges and quizzes
  • Track and share your progress with employers
  • Connect to mentorship and career opportunities