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>
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();
}
}
}
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!
<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!