---
schema_version: "1"
id: d4a8c9f3d1f6290e9f1ef3cd5427ba779b9449332260967c6551a08bec74daf5
title: Modal
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/lightning-base-components/lightning-design-system/guides/lwc/components/lightning/modal/lightning-modal.md
canonical_url: https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-modal.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 490f7beb8c533dd29687bf284b1b4dfe2aa51c4d210fb592734467ada0ed5dbd
products:
  - Lightning Component Reference
---

# 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

::::::tabset{tabs='["Example", "Develop", "Specifications"]'}

:::::tab

::component-playground{component="modal" model="lwc" namespace="lightning"}


:::::

:::::tab

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.

**note:** The `<dialog>` HTML element isn't currently supported. Use `lightning/modal` when you want to show content in a modal dialog with SLDS styling.

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-body`
- `lightning-modal-header`
- `lightning-modal-footer`

## Design

Use `lightning/modal` with the `lightning-modal-*` components, which implement the modals blueprint in the Salesforce Lightning Design System (SLDS). The modals adapt to SLDS 1 or SLDS 2 styling based on the org's theme or the container app that you use.

|                | SLDS 1                                                            | SLDS 2                                                                                                          |
| :------------- | :---------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| **Design**     | [Modals](https://v1.lightningdesignsystem.com/components/modals/) | [Modals](https://www.lightningdesignsystem.com/components/modals/)                                              |
| **For Use In** | Lightning Experience, Standalone Lightning app                    | [Lightning Experience](https://help.salesforce.com/s/articleView?id=xcloud.customize_ui_enhancedlex.htm&type=5) |

## Usage

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.

```js
/* c/myModal.js */

import { api } from "lwc";
import LightningModal from "lightning/modal";

export default class MyModal extends LightningModal {
  @api content;

  handleOkay() {
    this.close("okay");
  }
}
```

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.

<!-- prettier-ignore-start -->
```html
<!-- c/myModal.html -->

<template>
    <lightning-modal-header label="My Modal Heading"></lightning-modal-header>
    <lightning-modal-body> Content: {content} </lightning-modal-body>
    <lightning-modal-footer>
        <lightning-button label="OK" onclick={handleOkay}></lightning-button>
    </lightning-modal-footer>
</template>
```
<!-- prettier-ignore-end -->

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.

## Open a Modal Instance

`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 that 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, set `label` in the `lightning-modal-header` component. If the modal doesn't have a header, set the `label` property when opening the modal.

- `size` - Determines how much of the viewport width the modal uses. Supported values are `small`, `medium`, `large`, and `full`. You can set the `size` attribute when you open the modal. You can't change the modal size after the modal is opened. Default value is `medium`. The height of the modal is determined by the amount of content in the `lightning-modal-body` component, and the `size` value. For more information on the `full` variant, see the [**Styling Variants**](#styling-variants) section.

- `description` - Sets the modal's accessible description. It uses the `aria-description` attribute where supported, or falls back to `aria-describedby`. If you set a `description` value, include the label name at the beginning of your description text. 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 can briefly set `disableClose` to true while a completed form is saving, so the user doesn't dismiss the modal early. See [**Use the `disableClose` Attribute**](#use-the-disableclose-attribute).

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`.

```js
/* c/myApp.js */

import { LightningElement } from "lwc";
import MyModal from "c/myModal";

export default class MyApp extends LightningElement {
  async handleClick() {
    const result = await MyModal.open({
      // `label` is not included here in this example.
      // it is set on lightning-modal-header instead
      size: "large",
      description: "Accessible description of modal's purpose",
      content: "Passed into content api",
    });
    // if modal closed with X button, promise returns result = 'undefined'
    // if modal closed with OK button, promise returns result = 'okay'
    console.log(result);
  }
}
```

The HTML template for this app contains a button that opens the modal and displays the result returned when the modal closes.

<!-- prettier-ignore-start -->
```html
<!-- c/myApp.html -->

<template>
    <lightning-button
        onclick={handleClick}
        aria-haspopup="dialog"
        label="Open My Modal">
    </lightning-button>
    <p>Result: {result}</p>
</template>
```
<!-- prettier-ignore-end -->

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.

```js
/* c/myApp.js */

import { LightningElement } from "lwc";
import MyModal from "c/myModal";

export default class MyPage extends LightningElement {
  handleOpenClick() {
    MyModal.open({
      // maps to developer-created `@api options`
      options: [
        { id: 1, label: "Option 1" },
        { id: 2, label: "Option 2" },
      ],
    }).then((result) => {
      console.log(result);
    });
  }
}
```

The modal dialog can then use this property. In this case, the modal creates buttons whose labels are provided in the `options` array.

```js
/* c/myModal.js */

import { api } from "lwc";
import LightningModal from "lightning/modal";

export default class MyModal extends LightningModal {
  // Data is passed to api properties via .open({ options: [] })
  @api options = [];
}
```

<!-- prettier-ignore-start -->
```html
<!-- c/myModal.html -->

<template>
    <lightning-modal-header label="My Modal Heading"></lightning-modal-header>
    <lightning-modal-body>
        Let's make some buttons!
        <template for:each={options} for:item="option">
            <lightning-button
              onclick={handleOptionClick}
              data-id={option.id}
              key={option.id}
            >
                {option.label}
            </lightning-button>
        </template>
    </lightning-modal-body>
</template>
```
<!-- prettier-ignore-end -->

## Close a Modal Instance

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 in 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.

```js
/* c/myModal.js */

import { api } from "lwc";
import LightningModal from "lightning/modal";

export default class MyModal extends LightningModal {
  // Data is passed to apis via .open({ options: [] })
  @api options = [];

  handleOptionClick(e) {
    const { target } = e;
    const { id } = target.dataset;
    // this.close() triggers closing the modal
    // the value of `id` is passed as the result
    this.close(id);
  }
}
```

<!-- prettier-ignore-start -->
```html
<!-- c/myModal.html -->

<template>
  <template for:each={options} for:item="option">
    <lightning-button
      onclick={handleOptionClick}
      data-id={option.id}
      key={option.id}
    >
      {option.label}
    </lightning-button>
  </template>
</template>
```
<!-- prettier-ignore-end -->

## Use the `disableClose` Attribute

`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](https://www.w3.org/TR/UNDERSTANDING-WCAG20/keyboard-operation-trapping.html) 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.

```js
/* c/myModalForm.js */
/* This is intended as a mock example, not as fully functional code */
/* NOTE: observe values set:
   this.disableClose (in JS),
   and this.saveInProcess (in JS and HTML)
*/
import { api } from "lwc";
import LightningModal from "lightning/modal";

export default class MyModalForm extends LightningModal {
  // formData is utilized for saving current form values
  formData = {};
  failureType = null;
  saveStatus = {};
  saveInProcess = false;

  handleCloseClick() {
    this.close("canceled");
  }

  closeModal() {
    // immediately exits, so no need to trigger
    // this.disableClose = false OR
    // this.saveInProcess = false;
    // modal is destroyed, and focus moves
    // back to originally clicked item that
    // generated the modal
    this.close("success");
  }

  mitigateSaveFailure() {
    // depending on how easily the failure can be resolved
    // you may need to immediately set disableClose = false
    if (this.failureType === "recoverable") {
      // no need to call this.disableClose = false
      // or this.saveInProgress = false yet
      tryToFixFailure();
    } else {
      // can't resolve the error
      // need to allow users to exit modal
      this.disableClose = false;
      this.saveInProcess = false;
      // mock function to indicate modal state
      // while still allowing user to exit
      // preventing keyboard trap
      reportUnresolvableError();
    }
  }

  async saveData() {
    // switches disabled state on buttons
    this.saveInProcess = true;
    const saveStatus = await sendData(this.formData);
    return saveStatus && saveStatus.success ? closeModal() : mitigateSaveFailure();
  }

  async handleSaveClick() {
    if (isValid(this.formData)) {
      // begin saving data, temporarily disable
      // LightningModal's close button
      // Be sure to reenable the close button, by setting
      // this.disableClose = false, IF further interaction
      // is desired before the modal closes
      this.disableClose = true;
      await saveData();
    } else {
      // function that display form errors based on data
      showFormErrors(this.formData);
    }
  }
}
```

<!-- prettier-ignore-start -->
```html
<!-- c/myModalForm.html -->

<template>
    <lightning-modal-header
      label="My Modal Heading"
    ></lightning-modal-header>
    <lightning-modal-body>
      <!-- form here -->
    </lightning-modal-body>
    <lightning-modal-footer>
      <button
        disabled={saveInProcess}
        onclick={handleCloseClick}
        aria-label="Cancel and close"
      >Cancel</button>
      <button
        disabled={saveInProcess}
        onclick={handleSaveClick}
      >Save</button>
    </lightning-modal-footer>
</template>
```
<!-- prettier-ignore-end -->

## Modal Events

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 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)](https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-enable.html) to be enabled in the Salesforce org. See the [**Modal Events with LWS and Lightning Locker**](#modal-events-with-lws-and-lightning-locker) section for more information.

For example, here's a custom `select` event dispatched from `MyModal`.

```js
/* c/myModal.js */
dispatchSelectEvent(e) {
  // e.target might represent an input with an id and value
  const { id, value } = e.target;
  const selectEvent = new CustomEvent('select', {
    detail: { id, value }
  });
  this.dispatchEvent(selectEvent);
}
```

The `open()` method in `myApp` defines the `onselect` event handler. This example uses `onselect` to proxy events that we want to pass through.

```js
/* c/myApp.js */

// ...
// Process the select event from within the modal
handleSelectEvent(detail) {
  const { id, value } = detail;
  console.log(`select event fired elem with id ${id} and value: ${value}`);
}

// ...
// Trigger visibility of the modal
handleOpenModal() {
  MyModal.open({
    label: 'Modal Title',
    size: 'large',
    description: 'Modal Title with brief description',
    // event triggered when new CustomEvent('select', {detail: {}});
    // occurs *from within* LightningModal.
    // see dispatchSelectEvent() in c/myModal.js above
    onselect: (e) => {
      // stop further propagation of the event
      e.stopPropagation();
      // hand off to separate function to process
      // result of the event (see above in this example)
      this.handleSelectEvent(e.detail);
      // or proxy to be handled above by dispatching
      // another custom event to pass on the event
      // this.dispatchEvent(e);
    }
  });
}
```

See [Create and Dispatch Events](https://developer.salesforce.com/docs/platform/lwc/guide/events-create-dispatch) in the LWC Dev Guide for more information about events.

## Modal Events with LWS and Lightning Locker

Modal events work as expected when Lightning Web Security (LWS) is enabled within a Salesforce org, as described in the [**Modal Events**](#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](https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-enable.html) in the Security for Lightning Components 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](https://developer.salesforce.com/docs/platform/lwc/guide/use-navigate-modal) in the LWC Dev Guide for an example.

## Modal Events with Aura

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**](#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.

1. 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.
2. To continuously communicate data out of the `LightningModal` while the modal remains open, use events created and passed in when opening the modal from `Modal.open({ size, title, onmyevent })`.

These extracting methods fit into the larger LWC Modal-to-Aura event workflow.

1. Pass any events that should occur within the modal through `LightningModal`'s `.open()` method
2. Have the `LightningModal` JavaScript code fire any custom events
3. In the topmost LWC component that opened the modal, create an event handler to process the events, including stopping propagation.
4. Fire a separate event containing the LWC-processed event details and send it to the Aura parent component.
5. Use an Aura-based event handler to handle and process the event.

For more information, see [Send Events to an Enclosing Aura Component](https://developer.salesforce.com/docs/platform/lwc/guide/events-sending-to-aura-components) and [Events Best Practices](https://developer.salesforce.com/docs/platform/lwc/guide/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](https://developer.salesforce.com/docs/atlas.en-us.communities_dev.meta/communities_dev/communities_dev_basics_templates.htm), 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`.

<!-- prettier-ignore-start -->
```html
/* c/myApp.cmp */
<aura:component>
    <!-- <c:myLwcTreeGridWithNavigation/> component is a child component
        inside of <c:myLwcAppModalLauncher/>
    -->
    <c:myLwcAppModalLauncher/>
</aura:component>
```
<!-- prettier-ignore-end -->

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.

<!-- prettier-ignore-start -->
```html
<!-- c/myLwcAppModalLauncher.html -->

<template>
    <lightning-button
        onclick={handleModalOpen}
        aria-haspopup="dialog"
        label="Launch Navigation Modal">
    </lightning-button>
</template>
```
<!-- prettier-ignore-end -->

```js
/* c/myLwcAppModalLauncher.js */

import { LightningElement, wire } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import MyModal from 'c/myModalWithTreeGridNavigation';

export default class MyLwcAppModalLauncher extends NavigationMixin(LightningElement) {

/// ... other modal code

handleModalOpen() {
    // open up the Modal
    MyModal.open({
        size: 'medium',
        heading: 'Navigate to Record Page',
        description: 'Navigate to a record page by clicking the row button',
        options: [{ data }, { data }],
    }).then((result) => {
        // when the LightningModal closes, result is whatever
        // data that was passed out using this.close({ data })
        if (result === null) {
            // do something else
        } else {
            // shouldNavigate is boolean, arbitrary
            // rowId is what's needed for lightning-navigation
            const { shouldNavigate, rowId } = result;
            if (shouldNavigate) {
                this.navigateToObjectHome(rowId);
            }
        }
    });
}

navigateToObjectHome(rowId) {
    // construct the page ref
    const pageRef = {
        type: 'standard__recordPage',
        attributes: {
            recordId: rowId,
            actionName: 'view'
        }
    }
    // then call the .Navigate method with the pageRef
    this[NavigationMixin.Navigate](pageRef);
}

/* ... other modal code */
```

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.

<!-- prettier-ignore-start -->
```html
<!-- c/myModalWithTreeGridNavigation.html -->
<template>
    <lightning-modal-header label="My Modal Heading">
        The modal tagline.
    </lightning-modal-header>
    <lightning-modal-body>
        <h2>Choose a record to navigate to:</h2>
        <!-- custom event listener when button clicked inside tree grid -->
        <div onnavselection={handleNavSelection}>
            <c-my-lwc-tree-grid-with-navigation></c-my-lwc-tree-grid-with-navigation>
        </div>
    </lightning-modal-body>
    <!-- no <lightning-modal-footer> in this example -->
</template>
```
<!-- prettier-ignore-end -->

```js
/* c/myModalWithTreeGridNavigation.js */
handleNavSelection(event) {
    event.stopPropagation();
    const { detail: { rowId } } = event;
    if (rowId) {
        // rowId value is important to lightning-navigation
        // passing this data out through the LightningModal's
        // .close() method, then handled in the .then((result) {})
        // see code above: c/myLwcAppModalLauncher.js
        this.close({ shouldNavigate: true, rowId });
    }
}
```

```js
/* c/myLwcTreeGridWithNavigation.js */

dispatchNavSelectionEvent(rowId) {
    // add rowId to event.detail
    // this custom event listener exists in
    // LightningModal body code, see code above:
    // c/myModalWithTreeGridNavigation.html
    const eventToFire = new CustomEvent('navselection', {
        detail: { rowId },
        bubbles: true,
        cancelable: false
    });
    // dispatch custom event
    // see event listener added to lightning-modal-body
    this.dispatchEvent(eventToFire);
}

handleRowButtonClick(event) {
    // simplified handler to get row.Id
    // from the tree grid row button that was clicked
    const row = event.detail.row;
    this.dispatchNavSelectionEvent(row.Id);
}
```

## Component Styling

Use a combination of styling variants and utility classes to customize your modal.

### Styling Variants

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 `label` property using `Modal.open({ label })` or you’ll get a console error.
- The `label` property is required for all variants of `LightningModal`. Assistive devices read the `label` value, 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 in `lightning-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](https://webaim.org/techniques/semanticstructure/#headings).

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.

### Utility Classes

The `LightningModal` component also supports the SLDS [Directional variant](https://v1.lightningdesignsystem.com/components/modals/#Directional) modal blueprint pattern.

The component uses a utility class to apply the directional variant.
To achieve the directional button layout, place the buttons in a `div` with the `slds-modal__footer_directional` class.

<!-- prettier-ignore-start -->
```html
<!-- c/modalDirectional.html -->

<template>
    <lightning-modal-body> Content: {content} </lightning-modal-body>
    <lightning-modal-footer>
        <div class=“slds-modal__footer_directional”>
            <lightning-button label="NO" onclick={handleNo}></lightning-button>
            <lightning-button label="OK" onclick={handleOkay}></lightning-button>
        </div>
    </lightning-modal-footer>
</template>
```
<!-- prettier-ignore-end -->

### Styling Hooks

The `lightning-modal-*` helper components support [styling hooks](https://v1.lightningdesignsystem.com/components/modals/#Styling-Hooks-Overview). 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 provide CSS custom properties that use the `--slds-c-*` prefix and they change styling for specific elements or properties of a component. Component styling hooks are supported for SLDS 1 only. See the [SLDS 1 component blueprints](https://v1.lightningdesignsystem.com/components/overview/) for available component styling hooks.

For more information, see [Style Components Using Lightning Design System Styling Hooks](https://developer.salesforce.com/docs/platform/lwc/guide/create-components-css-custom-properties) in the Lightning Web Components Developer Guide.


## Accessibility

Upon opening, the modal determines where to place focus as follows.

- When the modal has multiple steps, with a different subtitle in the modal header for each step, the modal component places focus on the subtitle text.
- When the modal doesn't have multiple steps and 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 based on
the [Global Focus Guidelines](https://v1.lightningdesignsystem.com/accessibility/guidelines/global-focus/#dialogs) 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](https://webaim.org/techniques/semanticstructure/#headings).

To include tagline text or link content for the header section, add it between the `<lightning-modal-header>` tags.

## Unit Testing

Writing unit tests for `LightningModal` requires two sets of tests.

1. Parent components that use `c/myModal` define 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.
2. The unit tests for `c/myModal` test 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 `c/myApp` 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.

<!-- prettier-ignore-start -->
```html
<!-- c/myApp.html -->
<button data-button onclick={handleClick}>Select Item</button>
<div data-result>{result}</div>
```
<!-- prettier-ignore-end -->

```js
/* myApp.spec.js */

import MyModal from 'c/myModal';
jest.mock('c/myModal');

test(() => {
    // Create and appendChild(element)

    const buttonEle = element.shadowRoot.querySelector('[data-button]');
    const resultEle = element.shadowRoot.querySelector('[data-result]');

    // Mock .open() to resolve result 'option1'
    MyModal.open = jest.fn().mockResolvedValue('option1');
    // Initial value
    expect(resultEle.textContent).toBe(undefined);
    // Click modal open button
    buttonEle.click();

    // Click handler render cycle
    await Promise.resolve();
    // Render cycle triggered by tracked value {result}
    await Promise.resolve();

    // Verify result is set in the prompt template
    expect(resultEle.textContent).toBe('option1');
    // Open triggered once
    expect(MyModal.open.mock.calls).toHaveLength(1);
})
```

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`](#shorthand-selectors-for-lightningmodal).

```js
/* myModal.spec.js */

import { createElement } from "lwc";
import MyModal from "c/myModal";

describe("c-my-modal", () => {
  afterEach(() => {
    while (document.body.firstChild) {
      document.body.removeChild(document.body.firstChild);
    }
  });

  it("should select body elements", async () => {
    const element = createElement("c-mymodal", {
      is: MyModal,
    });
    document.body.appendChild(element);

    // If element apis are modified after appendChild
    // await Promise.resolve();

    // using shorthand selector for modalBody
    // see list of selectors below
    expect(element.modalBody$("button").textContent).toBe("hello world");
  });
});
```

## 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 into `this.close(value)`
- `element.modalHeader$()` - querySelect a single node in `lightning-modal-header`
  - Usage: `element.modalHeader$('a')` Returns first link
- `element.modalHeader$$()` - querySelectAll multiple nodes in `lightning-modal-header`
  - Usage:
    - `elem.modalHeader$$()` Returns all nodes in header
    - `elem.modalHeader$$('a')` Returns all links in header
- `element.modalBody$()` - querySelect a single node in `lightning-modal-body`
  - Usage: `element.modalBody$('button')` Returns first button
- `element.modalBody$$()` - querySelectAll multiple nodes in `lightning-modal-body`
  - Usage:
    - `elem.modalBody$$()` Returns all nodes in body
    - `elem.modalBody$$('button, [data-button]')` Returns buttons in body
- `element.modalFooter$()` - querySelect a single node in `lightning-modal-footer`
  - Usage: `element.modalFooter$('button')` Returns first button
- `element.modalFooter$$()` - querySelectAll multiple nodes in `lightning-modal-footer`
  - Usage:
    - `elem.modalFooter$$()` Returns all nodes in footer
    - `elem.modalFooter$$('button, [data-button]')` Returns buttons in footer


:::::

:::::tab
::component-specification{component="modal" model="lwc" namespace="lightning"}

:::::

::::::
