Skip to content

How it works

Svelte Modals is a modal manager for Svelte. It provides APIs for opening and closing modals, but leaves functionality and styling of those modals up to you or your UI library. There are no modal components provided out of the box, it onlys renders your modal component with props to communicate its visibility.

The Modal Stack

Modals are opened using a LIFO (Last In, First Out) stack. When a new modal is opened, it hides any previously opened modals until it is closed. These are all rendered inside the <Modals /> component, which should be somewhere near the root of your app or layout.

App.svelte
<script>
import { Modals } from 'svelte-modals'
</script>
<Modals />

Opening modals

Open a modal at any time by calling modals.open

<script>
import { modals } from 'svelte-modals'
import ConfirmModal from '$lib/components/ConfirmModal.svelte'
import AlertModal from '$lib/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>

The isOpen prop

Opened modal components receive an isOpen prop to indicate whether it is the top modal or not. It is up to you how to handle it. It will always be true when first opened, but may become false if another modal is opened before it is closed.

MyModal.svelte
<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
-->
<div class:hidden={!isOpen} aria-hidden={!isOpen}>
<form>
<!-- ... -->
</form>
</div>
<style>
.hidden {
display: none;
}
</style>

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.

Modals that are closed will unmount.

<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 and resolve their corresponding modals.open with the given value.

ConfirmModal.svelte
<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 '$lib/components/ConfirmModal.svelte'
import AlertModal from '$lib/components/AlertModal.svelte'
let result = $state(undefined)
async function onclick() {
result = await modals.open(ConfirmModal, { message: 'Are you sure?' })
}
</script>
<button {onclick}>Open</button>
<p>
result: {result}
</p>

result:

If you are using Typescript, you can define the type of the value by using the ModalProps interface.

ConfirmModal.svelte
<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'