Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
Screenshot showing a custom LWC modal popup component overlaying a Salesforce record page.
LWC

LWC Modal: How to Build a Reusable Salesforce Modal Popup

Build a reusable LWC modal in Salesforce: markup, CSS, parent and child events, focus trap, and the LightningModal base class behind the modern, accessible pattern.

The short answer

A step-by-step build of a custom modal popup in Salesforce Lightning Web Components (LWC). It covers structuring the modal markup with SLDS classes and controlling visibility with a boolean property and JavaScript event handlers.

Key takeaways Build the modal UI in HTML with the standard Salesforce Lightning Design System (SLDS) classes: slds-modal, slds-modal__container and slds-backdrop. Wrap the modal markup in a conditional template directive bound to a boolean property to control whether it shows. Write JavaScript event handlers that flip that visibility property between true and false when a button is clicked. Put the @track decorator on the visibility property so the UI updates on its own when the value changes.

A modal popup is how you pull the user's attention onto one thing, either to show information or to collect input, while the rest of the page waits. In Salesforce Lightning Web Components you can build one that sits properly inside the rest of your application's UI. Here is the step-by-step.

Modal popup built in LWC Salesforce

Step 1: Create a new LWC component

Start with the component itself. Navigate to the LWC component's folder in your Salesforce org and click the "New" button, give your component a name, then click "Submit".

Step 2: Import the required modules

To build a modal popup you need two modules imported into your component's JavaScript file, LightningElement and track. Add the following line of code to that file:

import { LightningElement, track } from 'lwc';

Step 3: Define the modal content

Next comes the content the modal will display. That lives in an HTML template inside your component's file. Here is what the markup might look like:

<template>
  <template if:true={isModalOpen}>
    <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
      <div class="slds-modal__container">
        <header class="slds-modal__header">
          <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Modal Header</h2>
        </header>
        <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
          Modal Content
        </div>
        <footer class="slds-modal__footer">
          <lightning-button label="Cancel" onclick={closeModal}></lightning-button>
          <lightning-button label="Save" variant="brand" onclick={saveModal}></lightning-button>
        </footer>
      </div>
    </section>
    <div class="slds-backdrop slds-backdrop_open"></div>
  </template>
</template>

Step 4: Define the event handlers

To show and hide the modal popup you need event handlers. They fire on user actions, usually a button click. Here is what the JavaScript might look like:

export default class MyComponent extends LightningElement {
  @track isModalOpen = false;

  showModal() {
    this.isModalOpen = true;
  }

  hideModal() {
    this.isModalOpen = false;
  }
}

The @track decorator makes sure the isModalOpen property is tracked and updated in the UI when its value changes. The showModal() and hideModal() methods are what show and hide the modal popup, respectively.

Step 5: Trigger the modal popup

The final step is triggering the popup, which means adding a button or some other UI element that calls the event handlers you defined in Step 4. Here is what the markup might look like:

<template>
  <lightning-button label="Open Modal" onclick={openModal}></lightning-button>
  <template if:true={isModalOpen}>
    <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
      <div class="slds-modal__container">
        <header class="slds-modal__header">
          <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Modal Header</h2>
        </header>
        <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
          Modal Content
        </div>
        <footer class="slds-modal__footer">
          <lightning-button label="Cancel" onclick={closeModal}></lightning-button>
          <lightning-button label="Save" variant="brand" onclick={saveModal}></lightning-button>
        </footer>
      </div>
    </section>
    <div class="slds-backdrop slds-backdrop_open"></div>
  </template>
</template>

The button in this example calls its click handler when it is clicked, and the modal popup is displayed once the isModalOpen property is set to true.

Conclusion

Modal popups are an important tool in interface design, and that is the whole pattern behind one: SLDS markup, a single boolean in the component, and handlers on each end of it. It is enough to put a modal anywhere in your application's UI where you need the user looking at one thing at a time.

Frequently asked questions

What is an LWC modal?

An LWC modal is a Lightning Web Component that puts a dialog box over the rest of the page and blocks interaction with the content behind it until the user closes it. Confirmations, forms and detail views are the usual jobs. On a modern org (Winter '23+), extend the LightningModal base class and accessibility, focus management and SLDS styling come built in.

How do you create a modal in LWC?

Two patterns. (1) A custom modal: a child component with conditional template rendering, slds-modal CSS classes, and parent to child events for open and close. (2) The modern one: extend LightningModal from 'lightning/modal', which hands you headers, footers, focus trap and ARIA out of the box. That second pattern needs Spring '23+ and is the official Salesforce recommendation.

How do I close an LWC modal from a child component?

Dispatch a CustomEvent named 'close' from the child: this.dispatchEvent(new CustomEvent('close')). The parent listens with @event onclose={handleClose} and toggles a tracked boolean that controls the modal's visibility via if:true. With LightningModal, call this.close('ok') from inside the modal and the parent receives the result from await modal.open().

What CSS classes does an LWC modal need?

Three SLDS classes do the work: slds-modal for positioning, slds-modal__container for the box, and slds-backdrop for the dark overlay. Add slds-fade-in-open to the modal and slds-fade-in--open to the backdrop to make them visible. Once those classes are on, SLDS handles z-index and centering for you.

Can an LWC modal be reusable across multiple components?

Yes, and that is the recommended approach. Build the modal as a standalone child component (c-confirm-modal, say) that exposes @api properties like header and message, dispatches close and confirm events, and drops into any parent as <c-confirm-modal>. With LightningModal, the same modal component opens programmatically from any LWC via import { open } from 'lightning/modal'.

How do I prevent body scroll when an LWC modal is open?

LightningModal handles this automatically. For a custom modal, toggle a CSS class on document.body when the modal opens: document.body.classList.add('slds-modal-open'). Pair that with overflow: hidden in your CSS. Remember to remove the class on close. Forgetting to clean up is a common bug: the modal disappears and the page underneath stays broken.

Is LightningModal better than building a custom LWC modal?

For new orgs on Spring '23+, yes. LightningModal covers ARIA roles, focus trap, ESC-to-close and SLDS styling, all of which take 100+ lines of code to implement correctly in a custom modal. Write your own only when you need a unique visual treatment LightningModal cannot accommodate, or when you have to support older API versions.

Newsletter

One email every Tuesday

New guides, tool updates, and the release-note changes that break things.

No spam. Unsubscribe in one click.

Comments

Loading comments...

Leave a Comment