Skip to main content

How to Build a Modal Popup with an Overlay in Salesforce LWC

In plain words: A modal popup is a box that appears over your main screen, forcing the user to interact with it before they can return to the main page. In Salesforce Lightning Web Components (LWC), you can build a clean, custom modal using a simple combination of HTML for the structure, CSS for the dark background overlay, and a JavaScript boolean variable to control when it opens and closes.

Modals are essential UI elements. Whether you need to ask a user to confirm a deletion, display a complex data entry form, or show a critical warning, a modal popup with a darkened background overlay focuses the user's attention exactly where you need it.

In this guide, we will build a highly reusable, custom modal component from scratch and show you how to control it from a parent component.

Step 1: Scaffold the Component

First, open your terminal (or VS Code) and use the Salesforce CLI to generate a new component named modalPopupWithOverlay.

sf lightning generate component -n modalPopupWithOverlay -d force-app/main/default/lwc

Step 2: Create the HTML Structure

Open the modalPopupWithOverlay.html file. We are going to use an lwc:if directive (the modern replacement for if:true) to control visibility. The structure consists of a container, a dark overlay, and the actual white content box.

<template>
    <template lwc:if={showModal}>
        <div class="modal-container">
            <!-- The dark background -->
            <div class="modal-overlay" onclick={handleCloseModal}></div>
            
            <!-- The actual popup box -->
            <div class="modal-content">
                <h2>Important Information</h2>
                <p>This is your custom modal content!</p>
                <br/>
                <lightning-button label="Close" onclick={handleCloseModal}></lightning-button>
            </div>
        </div>
    </template>
</template>
UX Tip: Notice that we added an onclick event to the modal-overlay div. This is a common design pattern that allows users to close the modal simply by clicking anywhere on the dark background!

Step 3: Add CSS for the Overlay and Positioning

Open the modalPopupWithOverlay.css file. We need to use fixed positioning and a high z-index to ensure the modal floats above everything else on the Salesforce page.

/* The container holding both the overlay and the popup */
.modal-container {
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999; /* Ensures it sits on top of standard Salesforce headers */
}

/* The dark semi-transparent background */
.modal-overlay {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  background-color: rgba(0, 0, 0, 0.6);
  z-index: 1;
}

/* The white content box in the center */
.modal-content {
  background-color: #fff;
  padding: 2rem;
  border-radius: 8px;
  box-shadow: 0 4px 10px rgba(0,0,0,0.2);
  z-index: 2;
  min-width: 300px;
  text-align: center;
}

Step 4: JavaScript Logic for Opening and Closing

Open the modalPopupWithOverlay.js file. We need a boolean variable to track the state, and we need to expose our open/close methods so a parent component can trigger them using the @api decorator.

import { LightningElement, api } from 'lwc';

export default class ModalPopupWithOverlay extends LightningElement {
    // Controls the visibility of the HTML template
    showModal = false;

    // @api allows parent components to call this method
    @api 
    handleOpenModal() {
        this.showModal = true;
    }

    // @api allows parent components to call this method
    @api 
    handleCloseModal() {
        this.showModal = false;
    }
}

Step 5: Calling the Modal from a Parent Component

Now that your modal is built, it's time to use it! In any other LWC (the parent), you can drop your modal into the HTML and use a button to trigger it.

Parent HTML:

<template>
    <lightning-card title="Modal Tester">
        <div class="slds-m-around_medium">
            <lightning-button label="Open Modal" variant="brand" onclick={triggerModal}></lightning-button>
        </div>
    </lightning-card>

    <!-- Drop the child component here -->
    <c-modal-popup-with-overlay></c-modal-popup-with-overlay>
</template>

Parent JavaScript:

import { LightningElement } from 'lwc';

export default class ParentComponent extends LightningElement {
    
    triggerModal() {
        // Query the child component and fire its exposed @api method
        const modalComponent = this.template.querySelector('c-modal-popup-with-overlay');
        if (modalComponent) {
            modalComponent.handleOpenModal();
        }
    }
}
Developer Trap: QuerySelector Fails
If your this.template.querySelector() is returning null, double-check your spelling. Remember that camelCase component names (modalPopupWithOverlay) must be converted to kebab-case (c-modal-popup-with-overlay) when placed in HTML!
Core Takeaway: To make a modal truly reusable, use the <slot></slot> tag inside your modal-content div. This allows the parent component to pass dynamic HTML headers and paragraphs into the modal body without having to rewrite the modal logic!

Conclusion

Building a custom modal with a background overlay in LWC is a fantastic way to understand component communication and CSS positioning. By keeping the CSS strict with high z-indexes and exposing control methods via @api, you have created a robust, reusable UI element that can be deployed anywhere in your Salesforce org.

Happy coding!