Modal Components
Modals are opened using a “last in first out” stack. Each time a modal is opened it gets added to the top of the stack. When closed it unmounts and is removed, revealing the next modal in the stack (if there is one).
Each modal component receives an isOpen
prop to determine if it is the currently shown modal.
This should be used by your modal component to conditionally render or display itself.
<script> const { isOpen } = $props()</script>
<!-- in most cases you can do this -->{#if isOpen} <div> <!-- ... --> </div>{/if}
<!-- but if you prefer you can visually hide your modal instead, preserving state of any children components--><div class:hidden={!isOpen} aria-hidden={!isOpen}> <MyForm /></div>
<style> .hidden { display: none; }</style>
Opening Modals
To open a modal, import modals
and call modals.open(YourModalComponent, props)
.
<script> import { modals } from 'svelte-modals' import ConfirmModal from '../../components/ConfirmModal.svelte' import AlertModal from '../../components/AlertModal.svelte'
async function show() { const result = await modals.open(ConfirmModal, { message: 'Are you sure?' })
if (result === 'confirm') { modals.open(AlertModal, { message: 'You confirmed' }) } else { modals.open(AlertModal, { message: 'You cancelled' }) } }</script>
<button onclick={show}>Open</button>
Stacking Modals
You can call modals.open
any time to open a new modal. This will add the new modal to the top of the stack,
keeping the previous modals mounted but updating their isOpen
prop to false
.
<script> import { modals} from 'svelte-modals' import ConfirmModal from '../../components/ConfirmModal.svelte'
function openInfiniteModal() { modals.open(ConfirmModal, { message: 'Open another modal? This is modal #' + (modals.stack.length + 1), onconfirm: () => openInfiniteModal() }) }</script>
<button onclick={openInfiniteModal}>Open</button>
Closing Modals
From anywhere in your app you can call modals.close()
to close the top modal, modals.close(amount)
to
close the last given number of modals, or modals.closeAll()
to close all modals.
<script> import { modals } from 'svelte-modals'
// close the current modal modals.close()
// close the last 2 modals modals.close(2)
// close all modals modals.closeAll()</script>
The close() Prop
Modals receive a close
prop which will close the modal and resolve
their corresponding modals.open
with the given value.
<script> const { isOpen, close } = $props()</script>
{#if isOpen} <div> <!-- ... --> <button onclick={() => close('cancel')}>Cancel</button> <button onclick={() => close('confirm')}>Confirm</button> </div>{/if}
<script> import { modals } from 'svelte-modals' import ConfirmModal from '../../components/ConfirmModal.svelte' import AlertModal from '../../components/AlertModal.svelte'
async function show() { const result = await modals.open(ConfirmModal, { message: 'Are you sure?' })
modals.open(AlertModal, { message: 'You chose: ' + result }) }</script>
<button onclick={show}>Open</button>
If you are using Typescript, you can define the type of the value by using the ModalProps
interface.
<script lang="ts"> import type { ModalProps } from 'svelte-modals'
type Result = 'cancel' | 'confirm'
const { isOpen, close }: ModalProps<Result> = $props()</script>
{#if isOpen} <div> <!-- ... --> <button onclick={() => close('cancel')}>Cancel</button> <button onclick={() => close('confirm')}>Confirm</button> </div>{/if}
const result = await modals.open(ConfirmModal, { message: 'Are you sure?' })
result // 'cancel' | 'confirm'
Transitions
Transitions can be added to your modal components just like any other Svelte component. However they
must be global transitions because they are mounted and unmounted by the ModalStack
component.
<script> import { fade } from 'svelte/transition'
const { isOpen, close, title, message } = $props()</script>
{#if isOpen} <div role="dialog" class="modal-container" transition:fade|global > <div class="modal-content"> <h2>{title}</h2> <p>{message}</p>
<div class="modal-actions"> <button onclick={() => close()}>OK</button> </div> </div> </div>{/if}
Transitions between Modals
By default, when opening one modal after another the transitions for both will play at the same time. Depending on your animation this might be ok, but often it’s cleaner to transition one at a time.
To control this behaviour, you can forward on the onintrostart
and onoutroend
props to your modal.
This tells ModalStack
to wait for the transition to finish before opening the next modal.
<script> import { fade } from 'svelte/transition'
const { isOpen, onintrostart, onoutroend } = $props()</script>
{#if isOpen} <div role="dialog" transition:fade|global {onintrostart} {onoutroend} > <!-- ... --> </div>{/if}
Lazy Loading
Modal components can be lazy loaded with dynamic imports
import { modals } from 'svelte-modals'
modals.open(() => import('./AlertModal.svelte'), { title: 'Lazy Modal', message: 'This modal was loaded lazily'})
While the component is being imported, the <ModalStack />
component will render backdrop
and loading
snippets.
Snippets
Snippets can be passed in as props the same way you would with a regular Svelte 5 component.
<script> const { isOpen, close, content } = $props()</script>
{#if isOpen} <div> <!-- ... --> {@render content()} </div>{/if}
<script> import { modals } from 'svelte-modals' import SnippetModal from '../../components/SnippetModal.svelte'
function onclick() { modals.open(SnippetModal, { content }) }</script>
{#snippet content()} <div>Snippet content</div>{/snippet}
<button onclick={onclick}>Open Modal</button>
Props
Modal components receive the following props:
interface ModalProps<ReturnValue = any> extends Record<string, any> { // whether the modal is currently open isOpen: boolean
// the unique id of the modal id: string
// the index of the modal in the stack index: number
// closes the modal with an optional return value close: (value?: ReturnValue) => void
// for transitions, see Transitions section onintrostart: () => void onoutroend: () => void}
If you’re using Typescript, you should use this interface to define the props of your modal components.
<script lang="ts"> import type { ModalProps } from 'svelte-modals'
// optional type CloseValue = 'cancel' | 'confirm'
interface MyModalProps extends ModalProps<CloseValue> { title: string }
const { isOpen, title, close }: MyModalProps = $props()
function handleConfirm() { // uses CloseValue close('confirm') }</script>