# Modal

> Add dialogs to your site for lightboxes, user notifications, or completely custom content.

Source: https://bootstrap-svelte.vercel.app/components/modal — part of the Bootstrap Svelte documentation (index: https://bootstrap-svelte.vercel.app/llms.txt).

---

## Accessibility

- `Modal.Title` automatically labels the open dialog with `aria-labelledby`.
- When the modal opens, focus moves to the first focusable control inside the dialog, usually the dismiss button.
- Tab and Shift+Tab stay within the open dialog until it closes.
- Escape closes the dialog when `isKeyboardDismissible` is enabled, and closing returns focus to the opener.
- Use a visible title for every modal. If a custom title is not visible, pass an explicit accessible label to `Modal.Root`.

## Basic Example

The Modal component creates a dialog box that overlays the current page. It consists of several components that work together: `Modal.Root`, `Modal.Dialog`, `Modal.Content`, `Modal.Header`, `Modal.Title`, `Modal.Body`, and `Modal.Footer`.

```html
<script>
    let isModalShown = $state(false);
</script>

<Button colorVariant="primary" onclick={() => isModalShown = true}>
    Launch demo modal
</Button>

<Modal.Root isShown={isModalShown} onHidden={() => isModalShown = false}>
    <Modal.Dialog>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Modal title</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <p>Modal body text goes here.</p>
            </Modal.Body>
            <Modal.Footer>
                <Button colorVariant="secondary" onclick={() => isModalShown = false}>Close</Button>
                <Button colorVariant="primary">Save changes</Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>
```

## Static Backdrop

When you set `useBackdrop="static"`, the modal won't close when clicking outside it. This forces the user to interact with the modal dialog explicitly.

```html
<script>
    let isModalShown = $state(false);
</script>

<Button colorVariant="primary" onclick={() => isModalShown = true}>
    Launch static backdrop modal
</Button>

<Modal.Root useBackdrop="static" isShown={isModalShown} onHidden={() => isModalShown = false}>
    <Modal.Dialog>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Static backdrop modal</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <p>This modal has a static backdrop, meaning it doesn't close when clicking outside it.</p>
            </Modal.Body>
            <Modal.Footer>
                <Button colorVariant="secondary" onclick={() => isModalShown = false}>Close</Button>
                <Button colorVariant="primary">Understood</Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>
```

## Scrolling Long Content

When modals become too long for the user's viewport, they scroll independently. You can create scrollable modals by setting `isScrollable=true` on the Modal.Dialog component.

```html
<script>
    let isLongContentModalShown = $state(false);
    let isLongContentWithScrollableModalShown = $state(false);
</script>

<!-- Standard modal with long content -->
<Button colorVariant="primary" onclick={() => isLongContentModalShown = true}>
    Launch long content modal
</Button>

<Modal.Root isShown={isLongContentModalShown} onHidden={() => isLongContentModalShown = false}>
    <Modal.Dialog>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Long content modal</Modal.Title>
            </Modal.Header>
            <Modal.Body style="min-height: 100vh">
                <!-- Very long content here -->
                <p>This is some placeholder content to show the scrolling behavior for modals.</p>
            </Modal.Body>
            <Modal.Footer>
                <Button colorVariant="secondary" onclick={() => isLongContentModalShown = false}>Close</Button>
                <Button colorVariant="primary">Save changes</Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>

<!-- Scrollable modal -->
<Button colorVariant="primary" onclick={() => isLongContentWithScrollableModalShown = true}>
    Launch scrollable modal
</Button>

<Modal.Root isShown={isLongContentWithScrollableModalShown} onHidden={() => isLongContentWithScrollableModalShown = false}>
    <Modal.Dialog isScrollable={true}>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Scrollable modal</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <!-- Content that will scroll within the modal -->
                <p>This content will scroll within the modal while the header and footer remain fixed.</p>
            </Modal.Body>
            <Modal.Footer>
                <Button colorVariant="secondary" onclick={() => isLongContentWithScrollableModalShown = false}>Close</Button>
                <Button colorVariant="primary">Save changes</Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>
```

## Vertically Centered

Add `isVerticallyCentered=true` to the Modal.Dialog component to vertically center the modal in the viewport.

```html
<script>
    let isVerticallyCenteredModalShown = $state(false);
    let isVerticallyCenteredWithScrollableModalShown = $state(false);
</script>

<!-- Vertically centered modal -->
<Button colorVariant="primary" onclick={() => isVerticallyCenteredModalShown = true}>
    Vertically centered modal
</Button>

<Modal.Root isShown={isVerticallyCenteredModalShown} onHidden={() => isVerticallyCenteredModalShown = false}>
    <Modal.Dialog isVerticallyCentered={true}>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Vertically centered modal</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <p>This modal is vertically centered in the viewport.</p>
            </Modal.Body>
            <Modal.Footer>
                <Button colorVariant="secondary" onclick={() => isVerticallyCenteredModalShown = false}>Close</Button>
                <Button colorVariant="primary">Save changes</Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>

<!-- Vertically centered scrollable modal -->
<Button colorVariant="primary" onclick={() => isVerticallyCenteredWithScrollableModalShown = true}>
    Vertically centered scrollable modal
</Button>

<Modal.Root isShown={isVerticallyCenteredWithScrollableModalShown} onHidden={() => isVerticallyCenteredWithScrollableModalShown = false}>
    <Modal.Dialog isVerticallyCentered={true} isScrollable={true}>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Vertically centered scrollable modal</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <p>This modal is both vertically centered and scrollable.</p>
                <!-- Add more content to demonstrate scrolling -->
            </Modal.Body>
            <Modal.Footer>
                <Button colorVariant="secondary" onclick={() => isVerticallyCenteredWithScrollableModalShown = false}>Close</Button>
                <Button colorVariant="primary">Save changes</Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>
```

## Using the Grid System

You can use the Bootstrap grid system within modals by nesting `.container-fluid` within the `Modal.Body`. This gives you a flexible way to lay out modal content using the same grid system used elsewhere in Bootstrap.

```html
<script>
    let isGridModalShown = $state(false);
</script>

<Button colorVariant="primary" onclick={() => isGridModalShown = true}>
    Launch modal with grid
</Button>

<Modal.Root isShown={isGridModalShown} onHidden={() => isGridModalShown = false}>
    <Modal.Dialog>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Grid in Modal</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <div class="container-fluid">
                    <div class="row">
                        <div class="col-md-4">.col-md-4</div>
                        <div class="col-md-4 ms-auto">.col-md-4 .ms-auto</div>
                    </div>
                    <div class="row">
                        <div class="col-md-3 ms-auto">.col-md-3 .ms-auto</div>
                        <div class="col-md-2 ms-auto">.col-md-2 .ms-auto</div>
                    </div>
                    <div class="row">
                        <div class="col-md-6 ms-auto">.col-md-6 .ms-auto</div>
                    </div>
                    <div class="row">
                        <div class="col-sm-9">
                            Level 1: .col-sm-9
                            <div class="row">
                                <div class="col-8 col-sm-6">Level 2: .col-8 .col-sm-6</div>
                                <div class="col-4 col-sm-6">Level 2: .col-4 .col-sm-6</div>
                            </div>
                        </div>
                    </div>
                </div>
            </Modal.Body>
            <Modal.Footer>
                <Button colorVariant="secondary" onclick={() => isGridModalShown = false}>Close</Button>
                <Button colorVariant="primary">Save changes</Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>
```

## Toggle Between Modals

Toggle between multiple modals with some clever JavaScript by stacking them. Make sure to place the modal HTML in your code in the same order you'd like it to appear.

```html
<script>
    let isToggleBetweenTwoModals1Shown = $state(false);
    let isToggleBetweenTwoModals2Shown = $state(false);
</script>

<Button colorVariant="primary" onclick={() => isToggleBetweenTwoModals1Shown = true}>
    Open first modal
</Button>

<Modal.Root isShown={isToggleBetweenTwoModals1Shown} onHidden={() => isToggleBetweenTwoModals1Shown = false}>
    <Modal.Dialog isVerticallyCentered={true}>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Modal 1</Modal.Title>
            </Modal.Header>
            <Modal.Body>Show a second modal and hide this one with the button below.</Modal.Body>
            <Modal.Footer>
                <Button colorVariant="primary" onclick={() => { isToggleBetweenTwoModals1Shown = false; setTimeout(() => { isToggleBetweenTwoModals2Shown = true; }, 150); }}>
                    Open second modal
                </Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>

<Modal.Root isShown={isToggleBetweenTwoModals2Shown} onHidden={() => isToggleBetweenTwoModals2Shown = false}>
    <Modal.Dialog isVerticallyCentered={true}>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Modal 2</Modal.Title>
            </Modal.Header>
            <Modal.Body>Hide this modal and show the first with the button below.</Modal.Body>
            <Modal.Footer>
                <Button colorVariant="primary" onclick={() => { isToggleBetweenTwoModals2Shown = false; setTimeout(() => { isToggleBetweenTwoModals1Shown = true; }, 150); }}>
                    Back to first
                </Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>
```

## Remove Animation

For modals that simply appear rather than fade in, set the `useFade` property to `false`.

```html
<script>
    let isWithoutFadeModalShown = $state(false);
</script>

<Button colorVariant="primary" onclick={() => isWithoutFadeModalShown = true}>
    Launch modal without fade
</Button>

<Modal.Root isShown={isWithoutFadeModalShown} useFade={false} onHidden={() => isWithoutFadeModalShown = false}>
    <Modal.Dialog>
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Modal without fade animation</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <p>This modal appears without a fade animation.</p>
            </Modal.Body>
            <Modal.Footer>
                <Button colorVariant="secondary" onclick={() => isWithoutFadeModalShown = false}>Close</Button>
                <Button colorVariant="primary">Save changes</Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>
```

## Fullscreen Modal

Another override is the option to have a modal fullscreen at particular breakpoints with the `fullscreenOnBreakpoint` property.

Available `fullscreenOnBreakpoint` options:

- `"always"` - Always fullscreen on all screens
- `"sm"` - Fullscreen below sm breakpoint
- `"md"` - Fullscreen below md breakpoint
- `"lg"` - Fullscreen below lg breakpoint
- `"xl"` - Fullscreen below xl breakpoint
- `"xxl"` - Fullscreen below xxl breakpoint

```html
<script>
    let isFullscreenModalShown = $state(false);
</script>

<Button colorVariant="primary" onclick={() => isFullscreenModalShown = true}>
    Launch fullscreen modal
</Button>

<Modal.Root isShown={isFullscreenModalShown} onHidden={() => isFullscreenModalShown = false}>
    <Modal.Dialog fullscreenOnBreakpoint="lg">
        <Modal.Content>
            <Modal.Header isDismissible={true}>
                <Modal.Title level={1} class="fs-5">Full screen below lg</Modal.Title>
            </Modal.Header>
            <Modal.Body><p>This modal is fullscreen on viewports smaller than the lg breakpoint.</p></Modal.Body>
            <Modal.Footer>
                <Button colorVariant="secondary" onclick={() => isFullscreenModalShown = false}>Close</Button>
                <Button colorVariant="primary">Save changes</Button>
            </Modal.Footer>
        </Modal.Content>
    </Modal.Dialog>
</Modal.Root>

<!-- Other fullscreen options -->
<!-- fullscreenOnBreakpoint="always" - Always fullscreen on all screens -->
<!-- fullscreenOnBreakpoint="sm" - Fullscreen below sm breakpoint -->
<!-- fullscreenOnBreakpoint="md" - Fullscreen below md breakpoint -->
<!-- fullscreenOnBreakpoint="xl" - Fullscreen below xl breakpoint -->
<!-- fullscreenOnBreakpoint="xxl" - Fullscreen below xxl breakpoint -->
```

---

## API Reference

### Modal.Root Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `id` | `string` | Auto-generated | Unique identifier for the offcanvas |
| `isFocusable` | `boolean` | `true` | Determines if the modal should receive focus when shown. |
| `isKeyboardDismissible` | `boolean` | `true` | Determines if the modal can be dismissed using the Escape key. |
| `isShown` | `boolean` | `false` | Controls whether the modal is displayed or hidden. |
| `onHide` | `EventListener` | - | Callback function to execute when the modal begins to hide. |
| `onHidePrevented` | `EventListener` | - | Callback function to execute when the modal hide is prevented. |
| `onHidden` | `EventListener` | - | Callback function to execute when the modal has been hidden. |
| `onShow` | `EventListener` | - | Callback function to execute when the modal begins to show. |
| `onShown` | `EventListener` | - | Callback function to execute when the modal has been shown. |
| `useBackdrop` | `'static' \| boolean` | `true` | Controls the modal backdrop behavior. When set to `'static'`, the modal won't close when clicking outside it. |
| `useFade` | `boolean` | `true` | Controls whether the modal uses a fade animation when opening and closing. |

### Modal.Content Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `id` | `string` | Auto-generated | Unique identifier for the offcanvas |

### Modal.Dialog Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `fullscreenOnBreakpoint` | `'always' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'xxl'` | - | Makes the modal fullscreen below the specified breakpoint. |
| `id` | `string` | Auto-generated | Unique identifier for the offcanvas |
| `isScrollable` | `boolean` | `false` | When set to `true`, the modal body becomes scrollable while the header and footer remain fixed. |
| `isVerticallyCentered` | `boolean` | `false` | When set to `true`, the modal is vertically centered in the viewport. |
| `size` | `'sm' \| 'lg' \| 'xl'` | - | Controls the size of the modal. Default is medium size. |

### Modal.Footer Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `id` | `string` | Auto-generated | Unique identifier for the offcanvas |

### Modal.Header Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `id` | `string` | Auto-generated | Unique identifier for the offcanvas |
| `isDismissible` | `boolean` | `false` | When set to `true`, a close button (×) is added to the header that will close the modal. |

### Modal.Title Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `id` | `string` | Auto-generated | Unique identifier for the offcanvas |
| `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `4` | The heading level to use (h1-h6). |

### CSS Classes

These classes can be used to customize the modal components:

| Class | Applied to | Description |
| --- | --- | --- |
| `.modal` | Modal.Root | Main container for the modal. |
| `.modal-dialog` | Modal.Dialog | Container for modal content that positions the modal in the viewport. |
| `.modal-content` | Modal.Content | Container for the modal's header, body, and footer. |
| `.modal-header` | Modal.Header | Container for the modal title and close button. |
| `.modal-title` | Modal.Title | Styles the modal title. |
| `.modal-body` | Modal.Body | Container for modal content. |
| `.modal-footer` | Modal.Footer | Container for modal actions (usually buttons). |
| `.modal-dialog-scrollable` | Modal.Dialog | Makes the modal body scrollable. |
| `.modal-dialog-centered` | Modal.Dialog | Vertically centers the modal in the viewport. |
| `.modal-sm` | Modal.Dialog | Creates a small modal. |
| `.modal-lg` | Modal.Dialog | Creates a large modal. |
| `.modal-xl` | Modal.Dialog | Creates an extra-large modal. |
| `.modal-fullscreen` | Modal.Dialog | Creates a fullscreen modal. |
| `.modal-fullscreen-[breakpoint]-down` | Modal.Dialog | Creates a modal that's fullscreen below a specific breakpoint. |
