Home / lwc / lwc-lightning-modal
lwc-lightning-modal
LightningModal base class (Winter '23+): extending LightningModal, open() static method, modal headers/bodies/footers, close() with result, size variants, accessibility. NOT for lightning-dialog legacy patterns (deprecated) — use lwc/lwc-modal-and-overlay.
Open the skill folder on GitHub
LWC Lightning Modal
Activate when building a modal dialog in LWC — confirmation prompts, inline forms, multi-step wizards. LightningModal (GA Winter '23) replaces the earlier ad-hoc modal patterns and handles focus trap, accessibility, and lifecycle consistently.
Before Starting
- Extend
LightningModalfromlightning/modal— notLightningElement. - Modal components are opened via the static
open()method, not rendered in the parent template. - Close via
this.close(result)— the returned promise resolves withresult.
Core Concepts
Defining a modal component
import LightningModal from 'lightning/modal';
export default class MyConfirm extends LightningModal {
@api label;
handleOk() { this.close('ok'); }
handleCancel() { this.close('cancel'); }
}
Template uses <lightning-modal-header>, <lightning-modal-body>, <lightning-modal-footer>.
Opening the modal
import MyConfirm from 'c/myConfirm';
async handleOpen() {
const result = await MyConfirm.open({
label: 'Confirm Delete',
size: 'small',
description: 'Confirm deletion'
});
if (result === 'ok') { ... }
}
open() returns a Promise that resolves when this.close(value) is called.
Sizes
small, medium, large, full — set via the size option.
Accessibility
Header slot becomes the accessible label automatically. Focus trapping is built-in. First focusable element gets focus on open.
Passing data in / out
- In:
@api-decorated properties set via theopen()options object. - Out:
this.close(value)resolves the promise withvalue.
Common Patterns
Pattern: Confirmation dialog
const ok = await ConfirmModal.open({ label: 'Delete record?' });
if (ok === 'confirm') { /* delete */ }
Pattern: Inline form modal
Modal hosts a lightning-record-edit-form; on save, call this.close(recordId). Parent refreshes its data.
Pattern: Multi-step wizard
Maintain step state inside the modal class; each step is a different rendered fragment. Final step calls this.close(allCollectedData).
Decision Guidance
| Need | Mechanism |
|---|---|
| Confirmation or prompt | LightningModal |
| Inline-embedded panel | Regular LWC with SLDS panel classes |
| Full-screen takeover | LightningModal size='full' |
| Non-modal popover | SLDS popover utility CSS |
| Legacy dialog code | Migrate to LightningModal |
Recommended Workflow
- Create the modal component extending
LightningModal. - Define
@apiinputs for data passed in. - Template uses modal header / body / footer slots.
- Implement close paths with meaningful result values.
- Parent calls
await MyModal.open(options)and handles result. - Test keyboard navigation (Tab trap, Esc closes).
- Test screen reader announces modal label.
Review Checklist
- [ ] Extends LightningModal (not LightningElement)
- [ ] Uses modal header/body/footer slots for SLDS styling
- [ ] Header set via label or header slot for accessibility
- [ ] close() called with meaningful result values
- [ ] Parent handles user dismiss (no result / undefined)
- [ ] Focus trap verified; Esc closes
- [ ] Size option matches content density
- [ ] No direct DOM manipulation or raw z-index overrides
Salesforce-Specific Gotchas
- Modal component must not render in parent template. Use
open()only. - Dismissing via Esc or outside-click resolves with
undefined. Handle undefined as "cancelled." - API not available before Winter '23. Older orgs need alternative patterns.
Output Artifacts
| Artifact | Description |
|---|---|
| Modal LWC | Subclass extending LightningModal |
| Parent invocation helper | await MyModal.open(...) wrapper |
| A11y test checklist | Keyboard + screen-reader verification |
Related Skills
lwc/lwc-accessibility-patterns— a11y fundamentalslwc/lwc-forms-patterns— form-in-modal patternslwc/lwc-wizard-patterns— multi-step UX