Modal
lightning/modal
Extend this component to create a modal window overlayed on the current app window.
For Use In
Lightning Experience, Standalone Lightning App
The lightning/modal module provides the LightningModal component to create a modal window on top of the current app window. A modal interrupts a user’s workflow.
LightningModal implements the SLDS modals blueprint.
Create a modal component in response to a user action, such as clicking a button or link. The modal blocks interaction with everything else on the page until the user acts upon or dismisses the modal.
Unlike other components, this component doesn't use a lightning-modal tag or extend LightningElement. There is no lightning-modal component. Instead, you create a modal by extending LightningModal and using these helper lightning-modal-* components to provide a header, footer and the body of the modal.
lightning-modal-bodylightning-modal-headerlightning-modal-footer
To create a modal component, import LightningModal from lightning/modal. The component has access to the normal LWC resources as well as the special container, helper components, methods, and events of the lightning/modal module.
In this example, the content property passes data to the modal from the component that invokes it.
The modal’s HTML template uses helper lightning-modal-* components to provide a header, footer, and the body of the modal. In this example, the content for the modal body comes from the content property we defined in the modal's JavaScript file.
The lightning-modal-body component is required for the modal template.
The lightning-modal-header and lightning-modal-footer components are optional, but recommended. The lightning-modal-* components render in the order they appear in the template. Place the lightning-modal-body component after lightning-modal-header and before the lightning-modal-footer component.
LightningModal provides an .open() method which opens a modal and returns a promise that asynchronously resolves with the result of the user’s interaction with the modal.
Each invocation of a modal component’s .open() method creates a unique instance of the modal. You can think of a modal as a self-contained application that starts from scratch when it opens. It displays the content you pass in through the .open() method or that you set within the modal's HTML template.
When you close a modal, the modal instance is destroyed, not hidden. On close, the modal must save the user’s input data or pass it to the invoking component as the promise result. Otherwise, the data is lost when the modal instance is closed.
The .open() method lets you assign values to the modal's properties. LightningModal provides these properties.
-
label- Required. Sets the modal's title and assistive device label. If the modal has a header, setlabelin thelightning-modal-headercomponent. If the modal doesn't have a header, set thelabelproperty when opening the modal. -
size- Determines how much of the viewport width the modal uses. Supported values aresmall,medium,large, andfull. You can set thesizeattribute when you open the modal. You can't change the modal size after the modal is opened. Default value ismedium. The height of the modal is determined by the amount of content in thelightning-modal-bodycomponent, and thesizevalue. For more information on thefullvariant, see the Styling Variants section. -
description- Sets the modal's accessible description. It uses thearia-descriptionattribute where supported, or falls back toaria-describedby. If you set a custom description value, include the label name at the beginning of your description, because some screen readers only read the description, and not the label. -
disableClose- Prevents closing the modal by normal means like the ESC key, the close button, or.close(). For example, you could briefly setdisableCloseto true while a completed form is saving, so the user doesn't dismiss the modal early. See Use thedisableCloseAttribute.
In this example, the app opens the myModal component. It invokes the .open() method in a handleClick() function bound to the app button’s onclick attribute, and uses async and await keywords to handle the promise returned by .open(). This example overrides the size value by setting it to large and sets the modal’s user-defined property content.
The HTML template for this app contains a button that opens the modal and displays the result returned when the modal closes.
You can also use .open() to pass data from your invoking component into the modal with custom properties decorated with @api. These properties can be any type, such as a string or an object that’s an array of key/value pairs to be assigned to the new modal instance.
For example, this app component sets an options property to a set of key/value pairs in MyModal.open(). The promise is handled using an arrow function that logs the result to the console.
The modal dialog can then use this property. In this case, the modal creates buttons whose labels are provided in the options array.
Use this.close(result) to close the modal, where result is anything you want to return from the modal. The .close() operation is asynchronous to display a brief fade out animation before the modal is destroyed. The result data can’t be modified after the close operation begins.
You can also close the modal with the default close button, the X at the top right corner. Closing a modal like this is the same as calling this.close() with an undefined result, so any data input is lost.
This example shows the handler for some buttons inside a modal. When the user clicks the button, this.close(id) returns the option that they chose and the modal closes.
disableClose temporarily turns off the ability to dismiss the modal by normal means like the ESC key, the close button, or this.close(). For example, you could briefly set disableClose to true while a completed form is saving, so the user doesn't dismiss the modal early.
Preventing a modal's close should be a temporary state. Use disableClose for less than 5 seconds. Accessibility is affected if you trap keyboard focus inappropriately. See No Keyboard Trap for more information.
When the process using disableClose finishes successfully, dismiss the modal. If the process using disableClose fails, re-enable the ability to dismiss the modal with disableClose = false, or give feedback to help the user resolve the issue.
While disableClose is true, disable any processes or UI buttons that might call Modal.close().
This example shows how to use disableClose. It's not a fully functional example, but could be used in a complex modal component.
A modal can only fire events captured by the component that opened it, not the modal itself. Normal techniques can't catch events that fire from the modal, because the events bubble up to a top root element outside of the component that opened the modal.
To capture modal events, attach them in the .open() method invoked by the component that opens the modal.
Capturing modal events requires Lightning Web Security (LWS) to be enabled in the Salesforce org. See the Modal Events with LWS and Lightning Locker section for more information.
For example, here's a custom select event dispatched from MyModal.
The open() method in myApp defines the onselect event handler. This example uses onselect to proxy events that we want to pass through.
See Create and Dispatch Events in the LWC Dev Guide for more information about events.
Modal events work as expected when Lightning Web Security (LWS) is enabled within a Salesforce org, as described in the Modal Events section. If LWS isn't enabled in an org, Lightning Locker is in effect.
LWS is replacing Lightning Locker over time and is already enabled in many customer orgs. New orgs have LWS enabled by default. To enable LWS, see Enable Lightning Web Security in an Org in the Lightning Web Components Developer Guide.
Under Lightning Locker, when you fire events within LightningModal, the browser throws a TypeError related to dispatchEvent. If your modal component runs in an org that can’t enable LWS yet, the workaround is to wrap the code that calls dispatchEvent in a child component that extends LightningElement. Use the wrapper component as a child of one of the modal components in the modal template.
See Navigate From a Modal in the LWC Dev Guide for an example.
For LightningModal, only the top level LWC component or application can communicate to the parent Aura component or application layer with eventing. This topmost component is usually the one that opens the LightningModal. LightningModal's child components can't event to a parent Aura component or application layer.
All required eventing that should occur during the LightningModal's life cycle must be passed in when you call Modal.open(). See onselect within .open() in the Modal Events section.
If you want the Aura layer to respond to events within child components embedded in the LightningModal, use event bubbling to move any data that you want to make available to the Aura layer into the topmost LWC component that opened the modal. Then, send the event from the LWC component.
With this in mind, there are two suggested methods for extracting data from the LightningModal to the Aura layer.
- To only communicate data out of modal after it's closed, first, close the modal, and pass the data out with
this.close({ data }), and handle the data received in.then((result) { ... })where the Modal is initially opened. - To continuously communicate data out of the
LightningModalwhile the modal remains open, use events created and passed in when opening the modal fromModal.open({ size, title, onmyevent }).
These extracting methods fit into the larger LWC Modal-to-Aura event workflow.
- Pass any events that should occur within the modal through
LightningModal's.open()method - Have the
LightningModalJavaScript code fire any custom events - In the topmost LWC component that opened the modal, create an event handler to process the events, including stopping propagation.
- Fire a separate event containing the LWC-processed event details and send it to the Aura parent component.
- Use an Aura-based event handler to handle and process the event.
For more information, see Send Events to an Enclosing Aura Component and Events Best Practices.
Let's see this workflow in action. In this example, we'll create a button (lightning-button) that launches a modal (LightningModal) containing a tree grid component (lightning-tree-grid) with a button in each record row that automatically navigates our user to that record's page (lightning-navigation). This use case requires data passing between our LWC components and a parent Aura component.
Because lightning-navigation requires you to pass in a recordId to construct a page reference, we need to pass the record's data out of LightningModal, and then use the methods provided by lightning-navigation. With modal's limitations for event bubbling to the Aura layer, we can't just wrap the button or treegrid with lightning-navigation. We need to pull that data up to the topmost LWC component, where the LightningModal .open() is called, for our Aura wrapper to handle the navigation.
However, in Aura-based Experience Builder sites, the NavigationMixin event can reach the Aura layer without being affected by the modal's limitations.
So, we import the lightning-navigation component into the topmost LWC component that sits within the Aura layer by passing the data through LightningModal's close method, this.close({ data }). Then the parent LWC component invokes the methods provided by lightning-navigation, which handles the navigation away from the page after the modal exits. From a UX perspective, this also lets us close the modal first, since lightning-navigation takes the user away from the current page.
Note that this sample code isn't fully functional. We're only showing the relevant code that makes the lightning-navigation use case work with LightningModal.
Here's our Aura component, containing our topmost LWC component, myLwcAppModalLauncher.
The topmost component, myLwcAppModalLauncher, launches and handles events that occur within our modal (MyModal). myLwcAppModalLauncher is also where we import the lightning-navigation component and use the rowId handed up through modal's this.close({ ..., rowId }) function.
This template is our modal, containing a tree grid with the navigation buttons, named c-my-lwc-tree-grid-with-navigation. The handleNavSelection function in this component processes the rowId data for the modal component to pass to its parent.
NOTE:
- Due to the intentional changes introduced with Native Shadow, it is important to understand that styles outside of your modal's code will not be able to style elements within your modal.
- Don't import SLDS styles your web UI doesn't actually use. Adding extra CSS files will cause the performance of your web UI to be lowered
A common use case for LightningModal is to include a form and form elements with common SLDS layouts. While the individual Lightning Base Components will contain their own styles in Native Shadow, Lightning Design System form element styling for form layouts like the form element variants Stacked, Horizontal and Compound aren't present in Native Shadow, but still needed.
In order to address this, you can import the form element layout library, lightning/sldsFormElementUtils, into your LightningModal's implementation code, along with other commonly needed libaries.
Following the previous c/myModalWithTreeGridNavigation implementation example, you might import these styles in the following fashion.
The lightning-modal-header and lightning-modal-footer child components are optional, and you can choose to not include one or the other in your modal.
The headerless variant of LightningModal has these additional requirements.
- Must set a descriptive modal title with the
labelproperty usingModal.open({ label })or you’ll get a console error. - The
labelproperty is required for all variants ofLightningModal. Assistive devices read thelabelvalue, even though the headerless modal variant doesn't display the label. - Because this variant doesn't use
lightning-modal-header, you have to manually create an<h1>heading inlightning-modal-body. Provide accessible structure by starting with heading level<h1>and using levels up to<h6>appropriately. For more information, see Semantic Structure, Headings on WebAim.org.
You can also create a full-screen modal component by setting the size attribute to full. This variant resizes the modal to the full width and height of the viewport on screens up to 30em (~480 pixels or less), like mobile phone devices. On screens larger than 30em (~481 pixels or larger), like desktop monitors or tablets, a size=full modal has the same behavior as a modal with size=large set.
The LightningModal component also supports the SLDS Directional variant modal blueprint pattern.
To achieve the directional button layout, place the buttons in a div with the slds-modal__footer_directional class.
The lightning-modal-* helper components support styling hooks. The styling hooks for the template that invokes the helper components doesn't carry over to them, so you must style each helper component individually.
Component styling hooks use the --slds-c-* prefix and change styling for specific elements or properties of a component. Component-specific styling isn’t recommended, because it’s unsupported for SLDS 2, but existing customizations still work with the original SLDS. If you use component styling hooks, limit the components to SLDS themes until SLDS 2 and the Salesforce Cosmos theme become generally available. See Modals: Styling Hooks Overview for documentation on component-specific hooks for this component.
Upon opening, the modal determines where to place focus as follows.
- When the modal header is present, the modal component places the focus on the title text in the header.
- When a modal header isn’t present, the modal component places the focus on the first interactive element in the modal body, such as a link, button, or input field.
- When the modal body doesn’t have interactive elements, or the only interactive element is a tooltip, the modal component places focus on the close button.
The focus is determined according to the Global Focus Guidelines for Lightning Design System.
The lightning-modal-header component renders the label value in an <h1> element. If your modal uses the header, begin any additional heading levels in the modal with <h2> for accessibility. Provide accessible structure by using heading levels up to <h6> appropriately. For more information, see Semantic Structure, Headings on WebAim.org.
To include tagline text or link content for the header section, add it between the <lightning-modal-header> tags.
Writing unit tests for LightningModal requires two sets of tests.
- Parent components that use
c/myModaldefine a mocked result when.open()is called during a test run. This mock speeds up testing and prevents repetition while testing actions that trigger modals. - The unit tests for
c/myModaltest the end-to-end functionality of the modal as a self contained component.
This example is a mock of a component called myModal. The parent component uses the modal to mock the c/myModal component's .open method. The mock .open resolves a constant value without opening the MyModal component. This example uses a button to open a modal and set the result in the template.
This example contains the unit tests for the actual c/myModal component. The tests use shorthand selectors to make unit testing more concise. For more information, see Shorthand Selectors for LightningModal.
The stubs for LightningModal have shorthand selectors to make unit testing more concise. These are all the shorthand selectors for unit testing that you can use with LightningModal.
element.closeValue- Value passed intothis.close(value)element.modalHeader$()- querySelect a single node inlightning-modal-header- Usage:
element.modalHeader$('a')Returns first link
- Usage:
element.modalHeader$$()- querySelectAll multiple nodes inlightning-modal-header- Usage:
elem.modalHeader$$()Returns all nodes in headerelem.modalHeader$$('a')Returns all links in header
- Usage:
element.modalBody$()- querySelect a single node inlightning-modal-body- Usage:
element.modalBody$('button')Returns first button
- Usage:
element.modalBody$$()- querySelectAll multiple nodes inlightning-modal-body- Usage:
elem.modalBody$$()Returns all nodes in bodyelem.modalBody$$('button, [data-button]')Returns buttons in body
- Usage:
element.modalFooter$()- querySelect a single node inlightning-modal-footer- Usage:
element.modalFooter$('button')Returns first button
- Usage:
element.modalFooter$$()- querySelectAll multiple nodes inlightning-modal-footer- Usage:
elem.modalFooter$$()Returns all nodes in footerelem.modalFooter$$('button, [data-button]')Returns buttons in footer
- Usage: