Pain-Free Salesforce Modals With the New Lightning Modal Component

If you’re a Salesforce Developer, you’ve felt the pain of coding a custom modal. Learn how the new Winter ’23 Lightning Modal can ease your pain.


What We’ll Cover

  • The pain of creating custom modals in Salesforce.
  • An overview of the new Winter ’23 modal component and how it eases this pain.
  • A step-by-step guide on creating your own modal component using the new LightningModal module and template tags.

Traditionally, the custom modal solution has involved:

  1. Embedding lengthy SLDS modal HTML in an existing component.
  2. Using a Boolean attribute on the component to show or hide the modal box.
  3. Toggling this Boolean attribute to true or false based on an action on your component.

The Modal HTML tended to be verbose and repeated across multiple components unless you abstracted it into its own component with @api properties.

A Pain-Free Lightning Modal Component

With the introduction of the new Winter ’23 Lightning modal component, Salesforce provides a little medicine for our collective modal-itis (this is a real condition).

Instead of requiring copy/paste code from the Lightning Design System docs, the new component generates modal HTML for us with a few boilerplate lines of code. What used to look like this (sorry, proving a point):

<section role="dialog" tabindex="-1" aria-modal="true" aria-labelledby="modal-heading-01" class="slds-modal slds-fade-in-open">
  <div class="slds-modal__container">
    <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse">
      <svg class="slds-button__icon slds-button__icon_large" aria-hidden="true">
        <use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#close"></use>
      </svg>
      <span class="slds-assistive-text">Cancel and close</span>
    </button>
    <div class="slds-modal__header">
      <h1 id="modal-heading-01" class="slds-modal__title slds-hyphenate">Modal header</h1>
    </div>
    <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
      <p>Sit nulla est ex deserunt exercitation anim occaecat. Nostrud ullamco deserunt aute id consequat veniam incididunt duis in sint irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit officia tempor esse quis. Cillum sunt ad dolore quis
        aute consequat ipsum magna exercitation reprehenderit magna. Tempor cupidatat consequat elit dolor adipisicing.</p>
      <p>Dolor eiusmod sunt ex incididunt cillum quis nostrud velit duis sit officia. Lorem aliqua enim laboris do dolor eiusmod officia. Mollit incididunt nisi consectetur esse laborum eiusmod pariatur proident. Eiusmod et adipisicing culpa deserunt nostrud
        ad veniam nulla aute est. Labore esse esse cupidatat amet velit id elit consequat minim ullamco mollit enim excepteur ea.</p>
    </div>
    <div class="slds-modal__footer slds-modal__footer_directional">
      <button class="slds-button slds-button_neutral">Skip This Step</button>
      <button class="slds-button slds-button_brand">Save & Next</button>
    </div>
  </div>
</section>
<div class="slds-backdrop slds-backdrop_open" role="presentation"></div>

Now becomes this:

<template>
    <lightning-modal-header label="My Modal Heading"></lightning-modal-header>
    <lightning-modal-body>This is the modal’s contents.</lightning-modal-body>
    <lightning-modal-footer>
        <lightning-button label="OK" onclick={handleOkay}></lightning-button>
    </lightning-modal-footer>
</template>

And beyond paring down markup and spitting out HTML, the new Lightning modal component has built-in open() and close() methods for easy modal control.

Enough about the benefits. Here’s an example so you can see it in action.

Step-by-Step: Using the New Lightning Modal Component

For this example, we’ll expand on the component we created for our adding flavor to screen flows with LWC post. In that article, we built a simple Lightning Web Component that lets users select their favorite sauce (soy, ketchup, mustard, etc.) We then put that component in a screen flow.

This time, we won’t call the component from a flow. Instead, we’ll display and work with it using the new lightning modal component. Here’s a preview of the end result.

Sauce Picker Lightning Modal Preview
Sauce Picker Lightning Modal Preview

Step 1: Create a New LWC and Import the LightningModal Module

Using your editor of choice (we use VSCode), create a new Lightning Web Component. We called our component ‘newLightningModal’. Here’s our newLightningModal.js file.

import { LightningElement, api } from 'lwc';
import LightningModal from 'lightning/modal';

export default class NewLightningModal extends LightningModal {
    @api content;
    @api headerText;
    handleSave() {
        //This is where a save action can be performed
        this.close('save');
    }
    handleCancel() {
        //This is where a cancel action can be performed
        this.close('cancel');
    }
}

In this file, we first import the LightningModal module using:

import LightningModal from 'lightning/modal';

Make sure your class declaration extends LightningModal. If you don’t, you won’t see any errors, but your modal window won’t open.

export default class NewLightningModal extends LightningModal

We then add a couple of @api-decorated variables that the calling component will populate:

@api content;
@api headerText;

Finally, we list 2 methods: one to handle a save action and the other to handle cancel. The modal code can call these methods on a button click from the HTML file.

handleSave() {
    //This is where a save action can be performed
    this.close('save');
}
handleCancel() {
    //This is where a cancel action can be performed
    this.close('cancel');
}

Step 2: Add the New Lightning Modal Template Tags to Our HTML

Now, we add the template tags that generate the modal code.

<template>
    <lightning-modal-header label={headerText}></lightning-modal-header>
    <lightning-modal-body>
        <!-- add the sauce picker component from our earlier tutorial -->
        <c-flavor-chooser onflavorselect={handleFlavorSelect}>
        </c-flavor-chooser>
    </lightning-modal-body>
    <lightning-modal-footer>
        <lightning-button label="Save" onclick={handleSave}>
        </lightning-button
        <lightning-button label="Cancel" onclick={handleCancel}> 
        </lightning-button>
    </lightning-modal-footer>
</template>

As mentioned before, the lightning-modal-header and lightning-modal-footer tags are optional. If you decide to include the header tag, you must include the label.

In the lightning-modal-body tag, we embedded the sauce picker component from our previous tutorial.

Finally, in the lightning-modal-footer tag, we included buttons that call the built-in close() method on our modal.

Step 3: Create the Calling Component

First, create another Lightning Web Component. We called ours ‘newModalContainer’. Here’s the newModalContainer.js file:

import { LightningElement } from 'lwc';
import newLightningModal from 'c/newLightningModal';

export default class ModalContainer extends LightningElement {
    result;
    async handleShowModal() {
        this.result = await newLightningModal.open({
           size: 'medium',
           description: 'Lets a user pick a sauce.',
           content: 'Passed into content api parameter',
           headerText:'Pick a Sauce, Any Sauce.'
       });
       console.log(result);
   }
}

Our .js file starts off by importing the new lightning component modal we created in the first step.

import newLightningModal from 'c/newLightningModal';

After importing our modal, we create a method called handleShowModal() that will call the open() method and pass parameters to the modal.

async handleShowModal() {
        this.result = await newLightningModal.open({
           size: 'medium',
           description: 'Lets a user pick a sauce.',
           content: 'Passed into content api parameter',
           headerText:'Pick a Sauce, Any Sauce.'
       });

We can change the size between small, medium, and large. For the content, we can also pass in an array of objects rather than just a string.

And here’s the modalContainer.html file:

<template>
    <lightning-card title="What's Your Favorite Sauce?" icon-name="utility:food_and_drink">
        <div class="slds-var-m-around_medium">
        <lightning-button label="Pick One Now!" onclick={handleShowModal}></lightning-button>
        </div>
    </lightning-card>
</template>

From the lightning button on this page, call the handleShowModal() method to open the lightning modal.

Lastly, ensure that your modalContainer meta file exposes your component to the destination of your choice.

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>55.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

From there, drag your calling component to the appropriate Lightning page or app. In this case, we added the modalContainer component to a Lightning Home Page.

The calling component, modalContainer, on a Lightning Home Page.
The calling component, modalContainer, on a Lightning Home Page.
Lightning Modal component example.
After clicking the ‘Pick One Now’ button.

As you can see, a much better experience than the traditional way of creating modals. This can’t be abstracted out completely, and you will likely have more than one modal component. Still, it’s a step in the right direction to simplifying and reducing your excess modal code.

Discover more from Twenty and One

Subscribe now to keep reading and get access to the full archive.

Continue reading