# ion-modal

URL: https://ionicframework.com/docs/api/modal

**Encapsulation**: [shadow](/docs/reference/glossary.md#shadow)

A Modal is a dialog that appears on top of the app's content, and must be dismissed by the app before interaction can resume. It is useful as a select component when there are a lot of options to choose from, or when filtering items in a list, as well as many other use cases.

## Inline Modals (Recommended)

`ion-modal` can be used by writing the component directly in your template. This reduces the number of handlers you need to wire up in order to present the modal.

When using `ion-modal` with Angular, React, or Vue, the component you pass in will be destroyed when the modal is dismissed. As this functionality is provided by the JavaScript framework, using `ion-modal` without a JavaScript framework will not destroy the component you passed in. If this is a needed functionality, we recommend using the `modalController` instead.

**JavaScript**

```html
<ion-header>
  <ion-toolbar>
    <ion-title>Inline Modal</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open</ion-button>
  <p id="message">This modal example uses triggers to automatically open a modal when the button is clicked.</p>
  <ion-modal trigger="open-modal">
    <ion-header>
      <ion-toolbar>
        <ion-buttons slot="start">
          <ion-button onclick="cancel()">Cancel</ion-button>
        </ion-buttons>
        <ion-title>Welcome</ion-title>
        <ion-buttons slot="end">
          <ion-button onclick="confirm()" strong="true">Confirm</ion-button>
        </ion-buttons>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">
      <ion-item>
        <ion-input label="Enter your name" label-placement="stacked" type="text" placeholder="Your name"></ion-input>
      </ion-item>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');

  function cancel() {
    modal.dismiss(null, 'cancel');
  }

  function confirm() {
    const input = document.querySelector('ion-input');
    modal.dismiss(input.value, 'confirm');
  }

  modal.addEventListener('willDismiss', (event) => {
    if (event.detail.role === 'confirm') {
      const message = document.querySelector('#message');
      message.textContent = `Hello ${event.detail.data}!`;
    }
  });
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>Inline Modal</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open</ion-button>
  <p>{{ message }}</p>
  <ion-modal trigger="open-modal" (willDismiss)="onWillDismiss($event)">
    <ng-template>
      <ion-header>
        <ion-toolbar>
          <ion-buttons slot="start">
            <ion-button (click)="cancel()">Cancel</ion-button>
          </ion-buttons>
          <ion-title>Welcome</ion-title>
          <ion-buttons slot="end">
            <ion-button (click)="confirm()" [strong]="true">Confirm</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding">
        <ion-item>
          <ion-input
            label="Enter your name"
            labelPlacement="stacked"
            type="text"
            placeholder="Your name"
            [(ngModel)]="name"
          ></ion-input>
        </ion-item>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import {
  IonButton,
  IonButtons,
  IonContent,
  IonHeader,
  IonInput,
  IonItem,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';
import { OverlayEventDetail } from '@ionic/core/components';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [
    FormsModule,
    IonButton,
    IonButtons,
    IonContent,
    IonHeader,
    IonInput,
    IonItem,
    IonModal,
    IonTitle,
    IonToolbar,
  ],
})
export class ExampleComponent {
  @ViewChild(IonModal) modal!: IonModal;

  message = 'This modal example uses triggers to automatically open a modal when the button is clicked.';
  name!: string;

  cancel() {
    this.modal.dismiss(null, 'cancel');
  }

  confirm() {
    this.modal.dismiss(this.name, 'confirm');
  }

  onWillDismiss(event: CustomEvent<OverlayEventDetail>) {
    if (event.detail.role === 'confirm') {
      this.message = `Hello, ${event.detail.data}!`;
    }
  }
}
```

**React**

```tsx
import React, { useState, useRef } from 'react';
import {
  IonButtons,
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonItem,
  IonInput,
} from '@ionic/react';
import { OverlayEventDetail } from '@ionic/core/components';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);
  const input = useRef<HTMLIonInputElement>(null);

  const [message, setMessage] = useState(
    'This modal example uses triggers to automatically open a modal when the button is clicked.'
  );

  function confirm() {
    modal.current?.dismiss(input.current?.value, 'confirm');
  }

  function onWillDismiss(event: CustomEvent<OverlayEventDetail>) {
    if (event.detail.role === 'confirm') {
      setMessage(`Hello, ${event.detail.data}!`);
    }
  }

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Inline Modal</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open
        </IonButton>
        <p>{message}</p>
        <IonModal ref={modal} trigger="open-modal" onWillDismiss={(event) => onWillDismiss(event)}>
          <IonHeader>
            <IonToolbar>
              <IonButtons slot="start">
                <IonButton onClick={() => modal.current?.dismiss()}>Cancel</IonButton>
              </IonButtons>
              <IonTitle>Welcome</IonTitle>
              <IonButtons slot="end">
                <IonButton strong={true} onClick={() => confirm()}>
                  Confirm
                </IonButton>
              </IonButtons>
            </IonToolbar>
          </IonHeader>
          <IonContent className="ion-padding">
            <IonItem>
              <IonInput
                label="Enter your name"
                labelPlacement="stacked"
                ref={input}
                type="text"
                placeholder="Your name"
              />
            </IonItem>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>Inline Modal</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open</ion-button>
    <p>{{ message }}</p>
    <ion-modal ref="modal" trigger="open-modal" @willDismiss="onWillDismiss">
      <ion-header>
        <ion-toolbar>
          <ion-buttons slot="start">
            <ion-button @click="cancel()">Cancel</ion-button>
          </ion-buttons>
          <ion-title>Welcome</ion-title>
          <ion-buttons slot="end">
            <ion-button :strong="true" @click="confirm()">Confirm</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding">
        <ion-item>
          <ion-input
            label="Enter your name"
            label-placement="stacked"
            ref="input"
            type="text"
            placeholder="Your name"
          ></ion-input>
        </ion-item>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import {
    IonButtons,
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonItem,
    IonInput,
  } from '@ionic/vue';
  import { OverlayEventDetail } from '@ionic/core/components';
  import { ref } from 'vue';

  const message = ref('This modal example uses triggers to automatically open a modal when the button is clicked.');

  const modal = ref();
  const input = ref();

  const cancel = () => modal.value.$el.dismiss(null, 'cancel');

  const confirm = () => {
    const name = input.value.$el.value;
    modal.value.$el.dismiss(name, 'confirm');
  };

  const onWillDismiss = (event: CustomEvent<OverlayEventDetail>) => {
    if (event.detail.role === 'confirm') {
      message.value = `Hello, ${event.detail.data}!`;
    }
  };
</script>
```

### Using `isOpen`

The `isOpen` property on `ion-modal` allows developers to control the presentation state of the modal from their application state. This means when `isOpen` is set to `true` the modal will be presented and when `isOpen` is set to `false` the modal will be dismissed.

`isOpen` uses a one-way data binding, meaning it will not automatically be set to `false` when the modal is dismissed. Developers should listen for the `ionModalDidDismiss` or `didDismiss` event and set `isOpen` to `false`. The reason for this is it prevents the internals of `ion-modal` from being tightly coupled with the state of the application. With a one way data binding, the modal only needs to concern itself with the boolean value that the reactive variable provides. With a two way data binding, the modal needs to concern itself with both the boolean value as well as the existence of the reactive variable itself. This can lead to non-deterministic behaviors and make applications harder to debug.

**JavaScript**

```html
<ion-header>
  <ion-toolbar>
    <ion-title>Inline Modal</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button expand="block" onclick="modal.isOpen = true">Open</ion-button>
  <ion-modal>
    <ion-header>
      <ion-toolbar>
        <ion-title>Modal</ion-title>
        <ion-buttons slot="end">
          <ion-button onclick="modal.isOpen = false">Close</ion-button>
        </ion-buttons>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">
      <p>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Magni illum quidem recusandae ducimus quos
        reprehenderit. Veniam, molestias quos, dolorum consequuntur nisi deserunt omnis id illo sit cum qui. Eaque,
        dicta.
      </p>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>Inline Modal</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button expand="block" (click)="setOpen(true)">Open</ion-button>
  <ion-modal [isOpen]="isModalOpen">
    <ng-template>
      <ion-header>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button (click)="setOpen(false)">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding">
        <p>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Magni illum quidem recusandae ducimus quos
          reprehenderit. Veniam, molestias quos, dolorum consequuntur nisi deserunt omnis id illo sit cum qui. Eaque,
          dicta.
        </p>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonButton, IonButtons, IonContent, IonHeader, IonModal, IonTitle, IonToolbar } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonButtons, IonContent, IonHeader, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {
  isModalOpen = false;

  setOpen(isOpen: boolean) {
    this.isModalOpen = isOpen;
  }
}
```

**React**

```tsx
import React, { useState } from 'react';
import { IonButtons, IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage } from '@ionic/react';

function Example() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Inline Modal</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton expand="block" onClick={() => setIsOpen(true)}>
          Open
        </IonButton>
        <IonModal isOpen={isOpen}>
          <IonHeader>
            <IonToolbar>
              <IonTitle>Modal</IonTitle>
              <IonButtons slot="end">
                <IonButton onClick={() => setIsOpen(false)}>Close</IonButton>
              </IonButtons>
            </IonToolbar>
          </IonHeader>
          <IonContent className="ion-padding">
            <p>
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Magni illum quidem recusandae ducimus quos
              reprehenderit. Veniam, molestias quos, dolorum consequuntur nisi deserunt omnis id illo sit cum qui.
              Eaque, dicta.
            </p>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>Inline Modal</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button expand="block" @click="setOpen(true)">Open</ion-button>

    <ion-modal :is-open="isOpen">
      <ion-header>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button @click="setOpen(false)">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding">
        <p>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Magni illum quidem recusandae ducimus quos
          reprehenderit. Veniam, molestias quos, dolorum consequuntur nisi deserunt omnis id illo sit cum qui. Eaque,
          dicta.
        </p>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import { IonButtons, IonButton, IonModal, IonHeader, IonToolbar, IonContent, IonTitle } from '@ionic/vue';
  import { ref } from 'vue';

  const isOpen = ref(false);

  const setOpen = (open: boolean) => (isOpen.value = open);
</script>
```

## Controller Modals

With the `modalController` developers can present an `ion-modal` programmatically. Developers will have complete control over when a modal is presented and dismissed.

**JavaScript**

```html
<ion-header>
  <ion-toolbar>
    <ion-title>Controller Modal</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button expand="block" onclick="openModal()">Open</ion-button>
  <p id="message">This modal example uses the modalController to present and dismiss modals.</p>
</ion-content>

<script>
  var openModal = async () => {
    const div = document.createElement('div');
    div.innerHTML = `
      <ion-header>
        <ion-toolbar>
          <ion-buttons slot="start">
            <ion-button color="medium" onclick="cancel()">Cancel</ion-button>
          </ion-buttons>
          <ion-title>Welcome</ion-title>
          <ion-buttons slot="end">
            <ion-button onclick="confirm()" strong>Confirm</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding">
        <ion-item>
          <ion-input type="text" label-placement="stacked" label="Enter your name" placeholder="Your name"></ion-input>
        </ion-item>
      </ion-content>
      `;

    const modal = await modalController.create({
      component: div,
    });

    modal.present();

    const { data, role } = await modal.onWillDismiss();

    if (role === 'confirm') {
      document.querySelector('#message').innerHTML = `Hello, ${data}!`;
    }
  };

  function cancel() {
    modalController.dismiss(null, 'cancel');
  }

  function confirm() {
    const input = document.querySelector('ion-input');
    modalController.dismiss(input.value, 'confirm');
  }
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>Controller Modal</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button expand="block" (click)="openModal()">Open</ion-button>
  <p>{{ message() }}</p>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { IonButton, IonContent, IonHeader, IonTitle, IonToolbar, ModalController } from '@ionic/angular';

import { ModalExampleComponent } from './modal-example.component';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [FormsModule, IonButton, IonContent, IonHeader, IonTitle, IonToolbar],
})
export class ExampleComponent {
  readonly message = signal('This modal example uses the modalController to present and dismiss modals.');

  constructor(private modalCtrl: ModalController) {}

  async openModal() {
    const modal = await this.modalCtrl.create({
      component: ModalExampleComponent,
    });
    modal.present();

    const { data, role } = await modal.onWillDismiss();

    if (role === 'confirm') {
      this.message.set(`Hello, ${data}!`);
    }
  }
}
```

`src/app/modal-example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-button color="medium" (click)="cancel()">Cancel</ion-button>
    </ion-buttons>
    <ion-title>Welcome</ion-title>
    <ion-buttons slot="end">
      <ion-button (click)="confirm()" [strong]="true">Confirm</ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-item>
    <ion-input labelPlacement="stacked" label="Enter your name" [(ngModel)]="name" placeholder="Your name"></ion-input>
  </ion-item>
</ion-content>
```

`src/app/modal-example.component.ts`

```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

import {
  IonButton,
  IonButtons,
  IonContent,
  IonHeader,
  IonInput,
  IonItem,
  IonTitle,
  IonToolbar,
  ModalController,
} from '@ionic/angular';

@Component({
  selector: 'app-modal-example',
  templateUrl: 'modal-example.component.html',
  imports: [FormsModule, IonButton, IonButtons, IonContent, IonHeader, IonInput, IonItem, IonTitle, IonToolbar],
})
export class ModalExampleComponent {
  name!: string;

  constructor(private modalCtrl: ModalController) {}

  cancel() {
    return this.modalCtrl.dismiss(null, 'cancel');
  }

  confirm() {
    return this.modalCtrl.dismiss(this.name, 'confirm');
  }
}
```

**React**

```tsx
import React, { useState, useRef } from 'react';
import {
  IonButtons,
  IonButton,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonItem,
  IonInput,
  useIonModal,
} from '@ionic/react';
import { OverlayEventDetail } from '@ionic/core/components';

const ModalExample = ({ dismiss }: { dismiss: (data?: string | null | undefined | number, role?: string) => void }) => {
  const inputRef = useRef<HTMLIonInputElement>(null);
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonButtons slot="start">
            <IonButton color="medium" onClick={() => dismiss(null, 'cancel')}>
              Cancel
            </IonButton>
          </IonButtons>
          <IonTitle>Welcome</IonTitle>
          <IonButtons slot="end">
            <IonButton onClick={() => dismiss(inputRef.current?.value, 'confirm')} strong={true}>
              Confirm
            </IonButton>
          </IonButtons>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonItem>
          <IonInput ref={inputRef} labelPlacement="stacked" label="Enter your name" placeholder="Your name" />
        </IonItem>
      </IonContent>
    </IonPage>
  );
};

function Example() {
  const [present, dismiss] = useIonModal(ModalExample, {
    dismiss: (data?: string | number | null, role?: string) => dismiss(data, role),
  });
  const [message, setMessage] = useState('This modal example uses the modalController to present and dismiss modals.');

  function openModal() {
    present({
      onWillDismiss: (event: CustomEvent<OverlayEventDetail>) => {
        if (event.detail.role === 'confirm') {
          setMessage(`Hello, ${event.detail.data}!`);
        }
      },
    });
  }

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Controller Modal</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton expand="block" onClick={() => openModal()}>
          Open
        </IonButton>
        <p>{message}</p>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

`src/components/Example.vue`

```html
<template>
  <ion-page>
    <ion-header>
      <ion-toolbar>
        <ion-title>Controller Modal</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">
      <ion-button expand="block" @click="openModal">Open Modal</ion-button>
      <p>{{ message }}</p>
    </ion-content>
  </ion-page>
</template>

<script setup lang="ts">
  import { IonButton, IonContent, IonPage, IonHeader, IonToolbar, IonTitle, modalController } from '@ionic/vue';
  import Modal from './Modal.vue';
  import { ref } from 'vue';

  const message = ref('This modal example uses the modalController to present and dismiss modals.');

  const openModal = async () => {
    const modal = await modalController.create({
      component: Modal,
    });

    modal.present();

    const { data, role } = await modal.onWillDismiss();

    if (role === 'confirm') {
      message.value = `Hello, ${data}!`;
    }
  };
</script>
```

`src/components/Modal.vue`

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-buttons slot="start">
        <ion-button color="medium" @click="cancel">Cancel</ion-button>
      </ion-buttons>
      <ion-title>Modal</ion-title>
      <ion-buttons slot="end">
        <ion-button @click="confirm" :strong="true">Confirm</ion-button>
      </ion-buttons>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-item>
      <ion-input label-placement="stacked" label="Enter your name" v-model="name" placeholder="Your name"></ion-input>
    </ion-item>
  </ion-content>
</template>

<script setup lang="ts">
  import {
    IonContent,
    IonHeader,
    IonTitle,
    IonToolbar,
    IonButtons,
    IonButton,
    IonItem,
    IonInput,
    modalController,
  } from '@ionic/vue';
  import { ref } from 'vue';

  const name = ref();

  const cancel = () => modalController.dismiss(null, 'cancel');
  const confirm = () => modalController.dismiss(name.value, 'confirm');
</script>
```

## Preventing a Modal from Dismissing

When entering data into a modal, it is often desirable to have a way of preventing accidental data loss. The `canDismiss` property on `ion-modal` gives developers control over when a modal is allowed to dismiss.

There are two different ways of using the `canDismiss` property: setting a boolean value or setting a callback function.

**Note**

Note: When using a sheet modal, `canDismiss` will not be checked on swipe if there is no `0` breakpoint set. However, it will still be checked when pressing `Esc` or the hardware back button.

### Setting a boolean value

Developers can set `canDismiss` to a boolean value. If `canDismiss` is `true`, then the modal will close when users attempt to dismiss the modal. If `canDismiss` is `false`, then the modal will not close when users attempt to dismiss the modal.

Setting a boolean value should be used when you need to require a particular action to be taken prior to a modal being dismissed. For example, if developers want to require that a "Terms of Use" checkbox is checked prior to closing the modal, they could set `canDismiss` to `false` initially and update it to `true` when the checkbox is checked.

**JavaScript**

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open</ion-button>

    <ion-modal trigger="open-modal">
      <ion-header>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button onclick="dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content>
        <p class="ion-padding-horizontal">You must accept the terms and conditions to close this modal.</p>
        <ion-item>
          <ion-checkbox id="terms">
            <div class="ion-text-wrap">Do you accept the terms and conditions?</div>
          </ion-checkbox>
        </ion-item>
      </ion-content>
    </ion-modal>
  </ion-content>
</div>

<script>
  var modal = document.querySelector('ion-modal');

  modal.canDismiss = false;
  modal.presentingElement = document.querySelector('.ion-page');

  function dismiss() {
    modal.dismiss();
  }

  modal.addEventListener('didPresent', () => {
    var terms = document.getElementById('terms');
    terms.addEventListener('ionChange', (event) => {
      modal.canDismiss = event.detail.checked;
    });
  });
</script>
```

**Angular**

`src/app/example.component.html`

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open</ion-button>
    <ion-modal #modal trigger="open-modal" [canDismiss]="canDismiss" [presentingElement]="presentingElement">
      <ng-template>
        <ion-header>
          <ion-toolbar>
            <ion-title>Modal</ion-title>
            <ion-buttons slot="end">
              <ion-button (click)="modal.dismiss()">Close</ion-button>
            </ion-buttons>
          </ion-toolbar>
        </ion-header>
        <ion-content>
          <p class="ion-padding-horizontal">You must accept the terms and conditions to close this modal.</p>
          <ion-item>
            <ion-checkbox id="terms" (ionChange)="onTermsChanged($event)" [checked]="canDismiss">
              <div class="ion-text-wrap">Do you accept the terms and conditions?</div>
            </ion-checkbox>
          </ion-item>
        </ion-content>
      </ng-template>
    </ion-modal>
  </ion-content>
</div>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  CheckboxCustomEvent,
  IonButton,
  IonButtons,
  IonCheckbox,
  IonContent,
  IonHeader,
  IonItem,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonButtons, IonCheckbox, IonContent, IonHeader, IonItem, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {
  canDismiss = false;

  presentingElement!: HTMLElement | null;

  ngOnInit() {
    this.presentingElement = document.querySelector('.ion-page');
  }

  onTermsChanged(event: CheckboxCustomEvent) {
    this.canDismiss = event.detail.checked;
  }
}
```

**React**

```tsx
import React, { useState, useRef, useEffect } from 'react';
import {
  IonButtons,
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonItem,
  IonCheckbox,
} from '@ionic/react';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);
  const page = useRef(undefined);

  const [canDismiss, setCanDismiss] = useState(false);
  const [presentingElement, setPresentingElement] = useState<HTMLElement | undefined>(undefined);

  useEffect(() => {
    setPresentingElement(page.current);
  }, []);

  function dismiss() {
    modal.current?.dismiss();
  }

  return (
    <IonPage ref={page}>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open
        </IonButton>
        <IonModal ref={modal} trigger="open-modal" canDismiss={canDismiss} presentingElement={presentingElement}>
          <IonHeader>
            <IonToolbar>
              <IonTitle>Modal</IonTitle>
              <IonButtons slot="end">
                <IonButton onClick={() => dismiss()}>Close</IonButton>
              </IonButtons>
            </IonToolbar>
          </IonHeader>
          <IonContent>
            <p className="ion-padding-horizontal">You must accept the terms and conditions to close this modal.</p>
            <IonItem>
              <IonCheckbox
                id="terms"
                checked={canDismiss}
                onIonChange={(event) => {
                  setCanDismiss(event.detail.checked);
                }}
              >
                <div className="ion-text-wrap">Do you accept the terms and conditions?</div>
              </IonCheckbox>
            </IonItem>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-page ref="page">
    <ion-header>
      <ion-toolbar>
        <ion-title>App</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">
      <ion-button id="open-modal" expand="block">Open</ion-button>

      <ion-modal ref="modal" trigger="open-modal" :can-dismiss="canDismiss" :presenting-element="presentingElement">
        <ion-header>
          <ion-toolbar>
            <ion-title>Modal</ion-title>
            <ion-buttons slot="end">
              <ion-button @click="dismiss()">Close</ion-button>
            </ion-buttons>
          </ion-toolbar>
        </ion-header>
        <ion-content>
          <p class="ion-padding-horizontal">You must accept the terms and conditions to close this modal.</p>
          <ion-item>
            <ion-checkbox id="terms" @ionChange="onTermsChanged" :checked="canDismiss">
              <div class="ion-text-wrap">Do you accept the terms and conditions?</div>
            </ion-checkbox>
          </ion-item>
        </ion-content>
      </ion-modal>
    </ion-content>
  </ion-page>
</template>

<script setup lang="ts">
  import {
    CheckboxCustomEvent,
    IonButtons,
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonItem,
    IonCheckbox,
    IonPage,
  } from '@ionic/vue';
  import { ref, onMounted } from 'vue';

  const canDismiss = ref(false);
  const presentingElement = ref();
  const page = ref();
  const modal = ref();

  const dismiss = () => {
    modal.value.$el.dismiss();
  };

  const onTermsChanged = (event: CheckboxCustomEvent) => {
    canDismiss.value = event.detail.checked;
  };

  onMounted(() => {
    presentingElement.value = page.value.$el;
  });
</script>
```

### Setting a callback function

Developers can set `canDismiss` to be a function. This function must return a `Promise` that resolves to either `true` or `false`. If the promise resolves to `true`, then the modal will dismiss. If the promise resolves to `false`, then the modal will not dismiss.

Setting a callback function should be used when you have complex dismissing criteria such as showing a confirmation dialog prior to dismissing the modal. The option that users select in this dialog can then be used to determine whether or not the modal should proceed with dismissing.

Note that setting a callback function will cause the swipe gesture to be interrupted when using a card or sheet modal. This is because Ionic does not know what your callback function will resolve to ahead of time.

**JavaScript**

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open</ion-button>

    <ion-modal trigger="open-modal" swipe-to-close="true">
      <ion-header>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button onclick="dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding">
        <p>You will be prompted when closing this modal.</p>
      </ion-content>
    </ion-modal>
  </ion-content>
</div>

<script>
  var modal = document.querySelector('ion-modal');

  modal.canDismiss = promptClose;
  modal.presentingElement = document.querySelector('.ion-page');

  function dismiss() {
    modal.dismiss();
  }

  async function promptClose() {
    const actionSheet = document.createElement('ion-action-sheet');

    actionSheet.header = 'Are you sure?';
    actionSheet.buttons = [
      {
        text: 'Yes',
        role: 'confirm',
      },
      {
        text: 'No',
        role: 'cancel',
      },
    ];
    document.body.appendChild(actionSheet);
    await actionSheet.present();

    const { role } = await actionSheet.onDidDismiss();

    return role === 'confirm';
  }
</script>
```

**Angular**

`src/app/example.component.html`

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open</ion-button>
    <ion-modal #modal trigger="open-modal" [canDismiss]="canDismiss" [presentingElement]="presentingElement">
      <ng-template>
        <ion-header>
          <ion-toolbar>
            <ion-title>Modal</ion-title>
            <ion-buttons slot="end">
              <ion-button (click)="modal.dismiss()">Close</ion-button>
            </ion-buttons>
          </ion-toolbar>
        </ion-header>
        <ion-content class="ion-padding">
          <p>You will be prompted when closing this modal.</p>
        </ion-content>
      </ng-template>
    </ion-modal>
  </ion-content>
</div>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  ActionSheetController,
  IonButton,
  IonButtons,
  IonContent,
  IonHeader,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonButtons, IonContent, IonHeader, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {
  presentingElement!: HTMLElement | null;

  constructor(private actionSheetCtrl: ActionSheetController) {}

  ngOnInit() {
    this.presentingElement = document.querySelector('.ion-page');
  }

  canDismiss = async () => {
    const actionSheet = await this.actionSheetCtrl.create({
      header: 'Are you sure?',
      buttons: [
        {
          text: 'Yes',
          role: 'confirm',
        },
        {
          text: 'No',
          role: 'cancel',
        },
      ],
    });

    actionSheet.present();

    const { role } = await actionSheet.onWillDismiss();

    return role === 'confirm';
  };
}
```

**React**

```tsx
import React, { useState, useRef, useEffect } from 'react';
import {
  IonButtons,
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  useIonActionSheet,
} from '@ionic/react';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);
  const page = useRef(null);

  const [presentingElement, setPresentingElement] = useState<HTMLElement | null>(null);
  const [present] = useIonActionSheet();

  useEffect(() => {
    setPresentingElement(page.current);
  }, []);

  function dismiss() {
    modal.current?.dismiss();
  }

  function canDismiss() {
    return new Promise<boolean>((resolve, reject) => {
      present({
        header: 'Are you sure?',
        buttons: [
          {
            text: 'Yes',
            role: 'confirm',
          },
          {
            text: 'No',
            role: 'cancel',
          },
        ],
        onWillDismiss: (event) => {
          if (event.detail.role === 'confirm') {
            resolve(true);
          } else {
            reject();
          }
        },
      });
    });
  }

  return (
    <IonPage ref={page}>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open
        </IonButton>
        <IonModal ref={modal} trigger="open-modal" canDismiss={canDismiss} presentingElement={presentingElement!}>
          <IonHeader>
            <IonToolbar>
              <IonTitle>Modal</IonTitle>
              <IonButtons slot="end">
                <IonButton onClick={() => dismiss()}>Close</IonButton>
              </IonButtons>
            </IonToolbar>
          </IonHeader>
          <IonContent className="ion-padding">
            <p>You will be prompted when closing this modal.</p>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-page ref="page">
    <ion-header>
      <ion-toolbar>
        <ion-title>App</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">
      <ion-button id="open-modal" expand="block">Open</ion-button>

      <ion-modal ref="modal" trigger="open-modal" :can-dismiss="canDismiss" :presenting-element="presentingElement">
        <ion-header>
          <ion-toolbar>
            <ion-title>Modal</ion-title>
            <ion-buttons slot="end">
              <ion-button @click="dismiss()">Close</ion-button>
            </ion-buttons>
          </ion-toolbar>
        </ion-header>
        <ion-content class="ion-padding">
          <p>You will be prompted when closing this modal.</p>
        </ion-content>
      </ion-modal>
    </ion-content>
  </ion-page>
</template>

<script setup lang="ts">
  import {
    IonButtons,
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonPage,
    actionSheetController,
  } from '@ionic/vue';
  import { ref, onMounted } from 'vue';

  const presentingElement = ref(undefined);
  const page = ref();
  const modal = ref();

  const dismiss = () => {
    modal.value.$el.dismiss();
  };

  const canDismiss = async () => {
    const actionSheet = await actionSheetController.create({
      header: 'Are you sure?',
      buttons: [
        {
          text: 'Yes',
          role: 'confirm',
        },
        {
          text: 'No',
          role: 'cancel',
        },
      ],
    });
    actionSheet.present();
    const { role } = await actionSheet.onWillDismiss();
    return role === 'confirm';
  };

  onMounted(() => {
    presentingElement.value = page.value.$el;
  });
</script>
```

### Prevent swipe to close

Developers may want to prevent users from swiping to close a card or sheet modal. This can be done by setting a callback function for `canDismiss` and checking if the `role` is not `gesture`.

**JavaScript**

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open</ion-button>

    <ion-modal trigger="open-modal">
      <ion-header>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button onclick="dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding">
        <p>
          To close this modal, please use the "Close" button provided. Note that swiping the modal will not dismiss it.
        </p>
      </ion-content>
    </ion-modal>
  </ion-content>
</div>

<script>
  var modal = document.querySelector('ion-modal');

  modal.canDismiss = (data, role) => role !== 'gesture';
  modal.presentingElement = document.querySelector('.ion-page');

  function dismiss() {
    modal.dismiss();
  }
</script>
```

**Angular**

`src/app/example.component.html`

```html
<div class="ion-page" #page>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open</ion-button>
    <ion-modal #modal trigger="open-modal" [canDismiss]="canDismiss" [presentingElement]="page">
      <ng-template>
        <ion-header>
          <ion-toolbar>
            <ion-title>Modal</ion-title>
            <ion-buttons slot="end">
              <ion-button (click)="modal.dismiss()">Close</ion-button>
            </ion-buttons>
          </ion-toolbar>
        </ion-header>
        <ion-content class="ion-padding">
          <p>
            To close this modal, please use the "Close" button provided. Note that swiping the modal will not dismiss
            it.
          </p>
        </ion-content>
      </ng-template>
    </ion-modal>
  </ion-content>
</div>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonButton, IonButtons, IonContent, IonHeader, IonModal, IonTitle, IonToolbar } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonButtons, IonContent, IonHeader, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {
  async canDismiss(data?: undefined, role?: string) {
    return role !== 'gesture';
  }
}
```

**React**

```tsx
import React, { useState, useRef, useEffect } from 'react';
import { IonButtons, IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage } from '@ionic/react';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);
  const page = useRef(null);

  const [presentingElement, setPresentingElement] = useState<HTMLElement | null>(null);

  useEffect(() => {
    setPresentingElement(page.current);
  }, []);

  function dismiss() {
    modal.current?.dismiss();
  }

  async function canDismiss(data?: any, role?: string) {
    return role !== 'gesture';
  }

  return (
    <IonPage ref={page}>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open
        </IonButton>
        <IonModal ref={modal} trigger="open-modal" canDismiss={canDismiss} presentingElement={presentingElement!}>
          <IonHeader>
            <IonToolbar>
              <IonTitle>Modal</IonTitle>
              <IonButtons slot="end">
                <IonButton onClick={() => dismiss()}>Close</IonButton>
              </IonButtons>
            </IonToolbar>
          </IonHeader>
          <IonContent className="ion-padding">
            <p>
              To close this modal, please use the "Close" button provided. Note that swiping the modal will not dismiss
              it.
            </p>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-page ref="page">
    <ion-header>
      <ion-toolbar>
        <ion-title>App</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">
      <ion-button id="open-modal" expand="block">Open</ion-button>

      <ion-modal ref="modal" trigger="open-modal" :can-dismiss="canDismiss" :presenting-element="page?.$el">
        <ion-header>
          <ion-toolbar>
            <ion-title>Modal</ion-title>
            <ion-buttons slot="end">
              <ion-button @click="dismiss">Close</ion-button>
            </ion-buttons>
          </ion-toolbar>
        </ion-header>
        <ion-content class="ion-padding">
          <p>
            To close this modal, please use the "Close" button provided. Note that swiping the modal will not dismiss
            it.
          </p>
        </ion-content>
      </ion-modal>
    </ion-content>
  </ion-page>
</template>

<script setup lang="ts">
  import { IonButtons, IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage } from '@ionic/vue';
  import { ref } from 'vue';

  const page = ref(null);
  const modal = ref(null);

  function dismiss() {
    modal.value.$el.dismiss();
  }

  async function canDismiss(data?: any, role?: string) {
    return role !== 'gesture';
  }
</script>
```

### Modifying dismiss behavior in child components

In certain scenarios, developers may need to customize the behavior of the `canDismiss` callback based on the state of a presented modal. This customization can be particularly useful, for instance, when developers want to prevent the modal from being dismissed if a form within it is invalid.

To achieve this customization, child components can employ various techniques such as function callbacks, event emission, or other reactivity mechanisms to communicate with the parent component and update the conditions governing the `canDismiss` callback.

Here's a simplified example illustrating how a child component can interact with a parent component to modify the `canDismiss` callback:

**Angular**

`src/app/example.component.html`

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open</ion-button>
    <ion-modal
      #modal
      trigger="open-modal"
      [canDismiss]="canDismiss"
      [presentingElement]="presentingElement"
      (willPresent)="onWillPresent()"
    >
      <ng-template>
        <app-child [modal]="modal" (dismissChange)="onDismissChange($event)"></app-child>
      </ng-template>
    </ion-modal>
  </ion-content>
</div>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import {
  ActionSheetController,
  IonButton,
  IonContent,
  IonHeader,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

import { ChildComponent } from './child.component';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [ChildComponent, FormsModule, IonButton, IonContent, IonHeader, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {
  presentingElement!: HTMLElement | null;

  private canDismissOverride = false;

  constructor(private actionSheetCtrl: ActionSheetController) {}

  ngOnInit() {
    this.presentingElement = document.querySelector('.ion-page');
  }

  onDismissChange(canDismiss: boolean) {
    // Allows the modal to be dismissed based on the state of the checkbox
    this.canDismissOverride = canDismiss;
  }

  onWillPresent() {
    // Resets the override when the modal is presented
    this.canDismissOverride = false;
  }

  canDismiss = async () => {
    if (this.canDismissOverride) {
      // Checks for the override flag to return early if we can dismiss the overlay immediately
      return true;
    }

    const actionSheet = await this.actionSheetCtrl.create({
      header: 'Are you sure?',
      buttons: [
        {
          text: 'Yes',
          role: 'confirm',
        },
        {
          text: 'No',
          role: 'cancel',
        },
      ],
    });

    actionSheet.present();

    const { role } = await actionSheet.onWillDismiss();

    return role === 'confirm';
  };
}
```

`src/app/child.component.html`

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>Modal</ion-title>
      <ion-buttons slot="end">
        <ion-button (click)="modal.dismiss()">Close</ion-button>
      </ion-buttons>
    </ion-toolbar>
  </ion-header>
  <ion-content>
    <ion-list>
      <ion-item>
        <ion-checkbox (ionChange)="checkboxChanged($event)">
          Override Dismiss<br />
          <ion-note class="ion-text-wrap"
            >Toggle the checkbox to allow immediately dismissing the modal without a prompt.</ion-note
          >
        </ion-checkbox>
      </ion-item>
    </ion-list>
  </ion-content>
</div>
```

`src/app/child.component.ts`

```ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

import {
  CheckboxCustomEvent,
  IonButton,
  IonButtons,
  IonCheckbox,
  IonContent,
  IonHeader,
  IonItem,
  IonList,
  IonModal,
  IonNote,
  IonToolbar,
  IonTitle,
} from '@ionic/angular';

@Component({
  selector: 'app-child',
  templateUrl: 'child.component.html',
  imports: [IonButton, IonButtons, IonCheckbox, IonContent, IonHeader, IonItem, IonList, IonNote, IonToolbar, IonTitle],
})
export class ChildComponent {
  @Input() modal!: IonModal;

  @Output() dismissChange = new EventEmitter<boolean>();

  checkboxChanged(event: CheckboxCustomEvent) {
    const checked = event.detail.checked;

    this.dismissChange.emit(checked);
  }
}
```

**React**

`src/main.tsx`

```tsx
import React, { useState, useRef, useEffect } from 'react';
import {
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  useIonActionSheet,
} from '@ionic/react';

import Child from './Child';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);
  const page = useRef(null);
  const [canDismissOverride, setCanDismissOverride] = useState(false);

  const [presentingElement, setPresentingElement] = useState<HTMLElement | null>(null);
  const [present] = useIonActionSheet();

  useEffect(() => {
    setPresentingElement(page.current);
  }, []);

  function dismiss() {
    modal.current?.dismiss();
  }

  function onWillPresent() {
    // Resets the override when the modal is presented
    setCanDismissOverride(false);
  }

  async function canDismiss() {
    if (canDismissOverride) {
      // Checks for the override flag to return early if we can dismiss the overlay immediately
      return true;
    }
    return new Promise<boolean>((resolve, reject) => {
      present({
        header: 'Are you sure?',
        buttons: [
          {
            text: 'Yes',
            role: 'confirm',
          },
          {
            text: 'No',
            role: 'cancel',
          },
        ],
        onWillDismiss: (event) => {
          if (event.detail.role === 'confirm') {
            resolve(true);
          } else {
            reject();
          }
        },
      });
    });
  }

  return (
    <IonPage ref={page}>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open
        </IonButton>
        <IonModal
          ref={modal}
          trigger="open-modal"
          canDismiss={canDismiss}
          presentingElement={presentingElement!}
          onWillPresent={onWillPresent}
        >
          <Child
            dismiss={dismiss}
            dismissChange={(checked) => {
              setCanDismissOverride(checked);
            }}
          />
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

`src/Child.tsx`

```tsx
import {
  IonHeader,
  IonToolbar,
  IonTitle,
  IonButtons,
  IonButton,
  IonContent,
  IonList,
  IonItem,
  IonCheckbox,
  IonNote,
} from '@ionic/react';

import type { CheckboxCustomEvent } from '@ionic/core/components';

interface ChildProps {
  dismissChange: (checked: boolean) => void;
  dismiss: () => void;
}

function Child({ dismissChange, dismiss }: ChildProps) {
  const checkboxChanged = (event: CheckboxCustomEvent) => {
    const checked = event.detail.checked;
    dismissChange(checked);
  };

  return (
    <>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Modal</IonTitle>
          <IonButtons slot="end">
            <IonButton onClick={() => dismiss()}>Close</IonButton>
          </IonButtons>
        </IonToolbar>
      </IonHeader>
      <IonContent>
        <IonList>
          <IonItem>
            <IonCheckbox onIonChange={checkboxChanged}>
              Override Dismiss
              <br />
              <IonNote className="ion-text-wrap">
                Toggle the checkbox to allow immediately dismissing the modal without a prompt.
              </IonNote>
            </IonCheckbox>
          </IonItem>
        </IonList>
      </IonContent>
    </>
  );
}
export default Child;
```

**Vue**

`src/components/Example.vue`

```html
<template>
  <ion-page ref="page">
    <ion-header>
      <ion-toolbar>
        <ion-title>App</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">
      <ion-button id="open-modal" expand="block">Open</ion-button>

      <ion-modal
        ref="modal"
        trigger="open-modal"
        :can-dismiss="canDismiss"
        :presenting-element="presentingElement"
        @willPresent="willPresent"
      >
        <Child :modal="modal" @dismissChange="dismissChanged" />
      </ion-modal>
    </ion-content>
  </ion-page>
</template>

<script setup lang="ts">
  import {
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonPage,
    actionSheetController,
  } from '@ionic/vue';
  import Child from './Child.vue';
  import { onMounted, ref } from 'vue';

  const modal = ref();
  const page = ref();

  let canDismissOverride = false;

  const dismiss = () => {
    modal.value.dismiss();
  };

  const canDismiss = async () => {
    if (canDismissOverride) {
      // Checks for the override flag to return early if we can dismiss the overlay immediately
      return true;
    }
    const actionSheet = await actionSheetController.create({
      header: 'Are you sure?',
      buttons: [
        {
          text: 'Yes',
          role: 'confirm',
        },
        {
          text: 'No',
          role: 'cancel',
        },
      ],
    });
    actionSheet.present();
    const { role } = await actionSheet.onWillDismiss();
    return role === 'confirm';
  };

  const willPresent = () => {
    // Resets the override when the modal is presented
    canDismissOverride = false;
  };

  const dismissChanged = () => {
    // Allows the modal to be dismissed based on the state of the checkbox
    canDismissOverride = true;
  };

  onMounted(() => {
    presentingElement.value = page.value;
  });
</script>
```

`src/components/Child.vue`

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>Modal</ion-title>
      <ion-buttons slot="end">
        <ion-button @click="dismiss()">Close</ion-button>
      </ion-buttons>
    </ion-toolbar>
  </ion-header>
  <ion-content>
    <ion-list>
      <ion-item>
        <ion-checkbox @ionChange="checkboxChanged">
          Override Dismiss<br />
          <ion-note class="ion-text-wrap"
            >Toggle the checkbox to allow immediately dismissing the modal without a prompt.</ion-note
          >
        </ion-checkbox>
      </ion-item>
    </ion-list>
  </ion-content>
</template>

<script setup lang="ts">
  import {
    IonButtons,
    IonButton,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonList,
    IonItem,
    IonCheckbox,
    IonNote,
  } from '@ionic/vue';

  const props = defineProps(['modal']);
  const emit = defineEmits(['dismissChange']);

  const dismiss = () => {
    props.modal.$el.dismiss();
  };

  const checkboxChanged = (event) => {
    const checked = event.detail.checked;
    emit('dismissChange', checked);
  };
</script>
```

## Card Modal

Developers can create a card modal effect where the modal appears as a card stacked on top of your app's main content. To create a card modal, developers need to set the `presentingElement` property on `ion-modal`.

The `presentingElement` property accepts a reference to the element that should display under your modal. This is typically a reference to `ion-router-outlet`.

The `canDismiss` property can be used to control whether or not the card modal can be swiped to close.

**Note**

The card display style is only available on iOS.

**JavaScript**

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Card Modal</ion-button>

    <ion-modal trigger="open-modal">
      <ion-header>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button onclick="modal.dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ion-modal>
  </ion-content>
</div>

<script>
  var modal = document.querySelector('ion-modal');

  modal.presentingElement = document.querySelector('.ion-page');

  function dismiss() {
    modal.dismiss();
  }
</script>
```

**Angular**

`src/app/example.component.html`

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Card Modal</ion-button>

    <ion-modal #modal trigger="open-modal" [presentingElement]="presentingElement">
      <ng-template>
        <ion-header>
          <ion-toolbar>
            <ion-title>Modal</ion-title>
            <ion-buttons slot="end">
              <ion-button (click)="modal.dismiss()">Close</ion-button>
            </ion-buttons>
          </ion-toolbar>
        </ion-header>
        <ion-content>
          <ion-list>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Connor Smith</h2>
                <p>Sales Rep</p>
              </ion-label>
            </ion-item>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Daniel Smith</h2>
                <p>Product Designer</p>
              </ion-label>
            </ion-item>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Greg Smith</h2>
                <p>Director of Operations</p>
              </ion-label>
            </ion-item>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Zoey Smith</h2>
                <p>CEO</p>
              </ion-label>
            </ion-item>
          </ion-list>
        </ion-content>
      </ng-template>
    </ion-modal>
  </ion-content>
</div>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  IonAvatar,
  IonButton,
  IonButtons,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [
    IonAvatar,
    IonButton,
    IonButtons,
    IonContent,
    IonHeader,
    IonItem,
    IonLabel,
    IonList,
    IonModal,
    IonTitle,
    IonToolbar,
  ],
})
export class ExampleComponent {
  // Typically referenced to your ion-router-outlet
  presentingElement!: HTMLElement | null;

  ngOnInit() {
    this.presentingElement = document.querySelector('.ion-page');
  }
}
```

**React**

```tsx
import React, { useState, useRef, useEffect } from 'react';
import {
  IonButtons,
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonList,
  IonItem,
  IonLabel,
  IonAvatar,
} from '@ionic/react';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);
  const page = useRef(null);

  const [presentingElement, setPresentingElement] = useState<HTMLElement | null>(null);

  useEffect(() => {
    setPresentingElement(page.current);
  }, []);

  function dismiss() {
    modal.current?.dismiss();
  }

  return (
    <IonPage ref={page}>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open
        </IonButton>
        <IonModal ref={modal} trigger="open-modal" presentingElement={presentingElement!}>
          <IonHeader>
            <IonToolbar>
              <IonTitle>Modal</IonTitle>
              <IonButtons slot="end">
                <IonButton onClick={() => dismiss()}>Close</IonButton>
              </IonButtons>
            </IonToolbar>
          </IonHeader>
          <IonContent className="ion-padding">
            <IonList>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Connor Smith</h2>
                  <p>Sales Rep</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Daniel Smith</h2>
                  <p>Product Designer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Greg Smith</h2>
                  <p>Director of Operations</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Zoey Smith</h2>
                  <p>CEO</p>
                </IonLabel>
              </IonItem>
            </IonList>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-page ref="page">
    <ion-header>
      <ion-toolbar>
        <ion-title>App</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">
      <ion-button id="open-modal" expand="block">Open</ion-button>

      <ion-modal ref="modal" trigger="open-modal" :presenting-element="presentingElement">
        <ion-header>
          <ion-toolbar>
            <ion-title>Modal</ion-title>
            <ion-buttons slot="end">
              <ion-button @click="dismiss()">Close</ion-button>
            </ion-buttons>
          </ion-toolbar>
        </ion-header>
        <ion-content class="ion-padding">
          <ion-list>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Connor Smith</h2>
                <p>Sales Rep</p>
              </ion-label>
            </ion-item>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Daniel Smith</h2>
                <p>Product Designer</p>
              </ion-label>
            </ion-item>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Greg Smith</h2>
                <p>Director of Operations</p>
              </ion-label>
            </ion-item>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Zoey Smith</h2>
                <p>CEO</p>
              </ion-label>
            </ion-item>
          </ion-list>
        </ion-content>
      </ion-modal>
    </ion-content>
  </ion-page>
</template>

<script setup lang="ts">
  import {
    IonButtons,
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonItem,
    IonList,
    IonAvatar,
    IonLabel,
    IonPage,
  } from '@ionic/vue';
  import { ref, onMounted } from 'vue';

  const presentingElement = ref(null);
  const page = ref();
  const modal = ref();

  const dismiss = () => {
    modal.value.$el.dismiss();
  };

  onMounted(() => {
    presentingElement.value = page.value.$el;
  });
</script>
```

## Sheet Modal

**Info**

[Content](/docs/api/content.md) should be used inside of the sheet modal if you want your modal content to be scrollable.

Developers can create a sheet modal effect similar to the drawer components available in maps applications. To create a sheet modal, developers need to set the `breakpoints` and `initialBreakpoint` properties on `ion-modal`.

The `breakpoints` property accepts an array which states each breakpoint that the sheet can snap to when swiped. A `breakpoints` property of `[0, 0.5, 1]` would indicate that the sheet can be swiped to show 0% of the modal, 50% of the modal, and 100% of the modal. When the modal is swiped to 0%, the modal will be automatically dismissed. Note that the modal cannot be dismissed on swipe if no `0` breakpoint is included, but it can still be dismissed by pressing `Esc` or the hardware back button.

The `initialBreakpoint` property is required so that the sheet modal knows which breakpoint to start at when presenting. The `initialBreakpoint` value must also exist in the `breakpoints` array. Given a `breakpoints` value of `[0, 0.5, 1]`, an `initialBreakpoint` value of `0.5` would be valid as `0.5` is in the `breakpoints` array. An `initialBreakpoint` value of `0.25` would not be valid as `0.25` does not exist in the `breakpoints` array.

The `backdropBreakpoint` property can be used to customize the point at which the `ion-backdrop` will begin to fade in. This is useful when creating interfaces that have content underneath the sheet that should remain interactive. A common use case is a sheet modal that overlays a map where the map is interactive until the sheet is fully expanded.

**JavaScript**

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal trigger="open-modal" initial-breakpoint="0.25">
    <ion-content>
      <ion-searchbar placeholder="Search"></ion-searchbar>
      <ion-list>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Connor Smith</h2>
            <p>Sales Rep</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Daniel Smith</h2>
            <p>Product Designer</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Greg Smith</h2>
            <p>Director of Operations</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Zoey Smith</h2>
            <p>CEO</p>
          </ion-label>
        </ion-item>
      </ion-list>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');
  var searchBar = document.querySelector('ion-searchbar');

  modal.breakpoints = [0, 0.25, 0.5, 0.75];

  searchBar.addEventListener('click', () => {
    modal.setCurrentBreakpoint(0.75);
  });
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal #modal trigger="open-modal" [initialBreakpoint]="0.25" [breakpoints]="[0, 0.25, 0.5, 0.75]">
    <ng-template>
      <ion-content>
        <ion-searchbar placeholder="Search" (click)="modal.setCurrentBreakpoint(0.75)"></ion-searchbar>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  IonAvatar,
  IonButton,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonSearchbar,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [
    IonAvatar,
    IonButton,
    IonContent,
    IonHeader,
    IonItem,
    IonLabel,
    IonList,
    IonModal,
    IonSearchbar,
    IonTitle,
    IonToolbar,
  ],
})
export class ExampleComponent {}
```

**React**

```tsx
import React, { useRef } from 'react';
import {
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonList,
  IonItem,
  IonLabel,
  IonAvatar,
  IonSearchbar,
} from '@ionic/react';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Sheet Modal
        </IonButton>
        <IonModal ref={modal} trigger="open-modal" initialBreakpoint={0.25} breakpoints={[0, 0.25, 0.5, 0.75]}>
          <IonContent className="ion-padding">
            <IonSearchbar onClick={() => modal.current?.setCurrentBreakpoint(0.75)} placeholder="Search"></IonSearchbar>
            <IonList>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Connor Smith</h2>
                  <p>Sales Rep</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Daniel Smith</h2>
                  <p>Product Designer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Greg Smith</h2>
                  <p>Director of Operations</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Zoey Smith</h2>
                  <p>CEO</p>
                </IonLabel>
              </IonItem>
            </IonList>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

    <ion-modal ref="modal" trigger="open-modal" :initial-breakpoint="0.25" :breakpoints="[0, 0.25, 0.5, 0.75]">
      <ion-content class="ion-padding">
        <ion-searchbar @click="$refs.modal.$el.setCurrentBreakpoint(0.75)" placeholder="Search"></ion-searchbar>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import {
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonItem,
    IonList,
    IonAvatar,
    IonLabel,
    IonSearchbar,
  } from '@ionic/vue';
</script>
```

### Interacting with background content

**JavaScript**

```html
<style>
  .counter__section {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
</style>

<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <p>You can interact with the +/- buttons until the sheet is fully expanded.</p>

  <div class="counter__section">
    <ion-button id="decrement" onclick="decrement()">-</ion-button>
    <p id="counter">0</p>
    <ion-button id="increment" onclick="increment()">+</ion-button>
  </div>

  <ion-modal is-open="true" initial-breakpoint="0.25" backdrop-dismiss="false" backdrop-breakpoint="0.5">
    <ion-content>
      <ion-searchbar placeholder="Search"></ion-searchbar>
      <ion-list>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Connor Smith</h2>
            <p>Sales Rep</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Daniel Smith</h2>
            <p>Product Designer</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Greg Smith</h2>
            <p>Director of Operations</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Zoey Smith</h2>
            <p>CEO</p>
          </ion-label>
        </ion-item>
      </ion-list>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');
  var searchBar = document.querySelector('ion-searchbar');

  modal.breakpoints = [0, 0.25, 0.5, 0.75];

  searchBar.addEventListener('click', () => {
    modal.setCurrentBreakpoint(0.75);
  });

  function increment() {
    const counter = document.querySelector('#counter');
    counter.innerHTML = parseInt(counter.innerText) + 1;
  }

  function decrement() {
    const counter = document.querySelector('#counter');
    counter.innerHTML = parseInt(counter.innerText) - 1;
  }
</script>
```

**Angular**

`src/app/example.component.html`

```html
<div class="ion-page">
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <p>You can interact with the +/- buttons until the sheet is fully expanded.</p>

    <div class="counter__section">
      <ion-button (click)="decrement()">-</ion-button>
      <p>{{count }}</p>
      <ion-button (click)="increment()">+</ion-button>
    </div>

    <ion-modal
      #modal
      trigger="open-modal"
      [isOpen]="true"
      [initialBreakpoint]="0.25"
      [breakpoints]="[0.25, 0.5, 0.75]"
      [backdropDismiss]="false"
      [backdropBreakpoint]="0.5"
    >
      <ng-template>
        <ion-content class="ion-padding">
          <ion-searchbar placeholder="Search" (click)="modal.setCurrentBreakpoint(0.75)"></ion-searchbar>
          <ion-list>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Connor Smith</h2>
                <p>Sales Rep</p>
              </ion-label>
            </ion-item>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Daniel Smith</h2>
                <p>Product Designer</p>
              </ion-label>
            </ion-item>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Greg Smith</h2>
                <p>Director of Operations</p>
              </ion-label>
            </ion-item>
            <ion-item>
              <ion-avatar slot="start">
                <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
              </ion-avatar>
              <ion-label>
                <h2>Zoey Smith</h2>
                <p>CEO</p>
              </ion-label>
            </ion-item>
          </ion-list>
        </ion-content>
      </ng-template>
    </ion-modal>
  </ion-content>
</div>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  IonAvatar,
  IonButton,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonSearchbar,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [
    IonAvatar,
    IonButton,
    IonContent,
    IonHeader,
    IonItem,
    IonLabel,
    IonList,
    IonModal,
    IonSearchbar,
    IonTitle,
    IonToolbar,
  ],
})
export class ExampleComponent {
  count = 0;

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}
```

`src/app/example.component.css`

```css
.counter__section {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
```

**React**

```tsx
import React, { useRef, useState } from 'react';
import {
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonList,
  IonItem,
  IonLabel,
  IonAvatar,
  IonSearchbar,
} from '@ionic/react';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);
  const [count, setCount] = useState(0);

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <p>You can interact with the +/- buttons until the sheet is fully expanded.</p>
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'space-between',
          }}
        >
          <IonButton onClick={() => setCount(count - 1)}>-</IonButton>
          <p>{count}</p>
          <IonButton onClick={() => setCount(count + 1)}>+</IonButton>
        </div>
        <IonModal
          ref={modal}
          trigger="open-modal"
          isOpen={true}
          initialBreakpoint={0.25}
          breakpoints={[0.25, 0.5, 0.75]}
          backdropDismiss={false}
          backdropBreakpoint={0.5}
        >
          <IonContent className="ion-padding">
            <IonSearchbar onClick={() => modal.current?.setCurrentBreakpoint(0.75)} placeholder="Search"></IonSearchbar>
            <IonList>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Connor Smith</h2>
                  <p>Sales Rep</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Daniel Smith</h2>
                  <p>Product Designer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Greg Smith</h2>
                  <p>Director of Operations</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Zoey Smith</h2>
                  <p>CEO</p>
                </IonLabel>
              </IonItem>
            </IonList>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<style>
  .counter__section {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
</style>

<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <p>You can interact with the +/- buttons until the sheet is fully expanded.</p>

    <div class="counter__section">
      <ion-button id="decrement" @click="count--">-</ion-button>
      <p>{{count}}</p>
      <ion-button id="increment" @click="count++">+</ion-button>
    </div>

    <ion-modal
      ref="modal"
      trigger="open-modal"
      :is-open="true"
      :initial-breakpoint="0.25"
      :breakpoints="[0.25, 0.5, 0.75]"
      :backdrop-dismiss="false"
      :backdrop-breakpoint="0.5"
    >
      <ion-content class="ion-padding">
        <ion-searchbar @click="$refs.modal.$el.setCurrentBreakpoint(0.75)" placeholder="Search"></ion-searchbar>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import {
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonItem,
    IonList,
    IonAvatar,
    IonLabel,
    IonSearchbar,
  } from '@ionic/vue';
  import { ref } from 'vue';

  const count = ref(0);
</script>
```

### Custom Sheet Height

Developers should use the `--height` CSS Variable to change the height of the sheet modal instead of changing the last breakpoint in the `breakpoints` array. The reason for this is changing the last breakpoint in the `breakpoints` array to a value less than `1` will cause some of the modal to be inaccessible outside of the viewport.

The following example shows how to get a sheet modal that is automatically sized based on its content. Note that by keeping the maximum breakpoint at `1` we ensure that the entire modal is accessible in the viewport.

**JavaScript**

```html
<style>
  .block {
    width: 100%;
    height: 300px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  ion-modal {
    --height: auto;
  }
</style>

<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal trigger="open-modal">
    <div class="block">Block of Content</div>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');

  modal.initialBreakpoint = 1;
  modal.breakpoints = [0, 1];
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal trigger="open-modal" [initialBreakpoint]="1" [breakpoints]="[0, 1]">
    <ng-template>
      <div class="block">Block of Content</div>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.css`

```css
.block {
  width: 100%;
  height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
}

ion-modal {
  --height: auto;
}
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonButton, IonContent, IonHeader, IonModal, IonTitle, IonToolbar } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonContent, IonHeader, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {}
```

**React**

`src/main.tsx`

```tsx
import React, { useRef } from 'react';
import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage } from '@ionic/react';

import './main.css';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Sheet Modal
        </IonButton>
        <IonModal ref={modal} trigger="open-modal" initialBreakpoint={1} breakpoints={[0, 1]}>
          <div className="block">Block of Content</div>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

`src/main.css`

```css
.block {
  width: 100%;
  height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
}

ion-modal {
  --height: auto;
}
```

**Vue**

```html
<style>
  .block {
    width: 100%;
    height: 300px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  ion-modal {
    --height: auto;
  }
</style>

<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

    <ion-modal trigger="open-modal" :initial-breakpoint="1" :breakpoints="[0, 1]">
      <div class="block">Block of Content</div>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle } from '@ionic/vue';
</script>
```

### Handle Behavior

Sheet modals can optionally render a handle indicator used for dragging the sheet between breakpoints. The `handleBehavior` property can be used to configure the behavior of when the handle is activated by the user.

**JavaScript**

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal trigger="open-modal" initial-breakpoint="0.25" handle-behavior="cycle">
    <ion-content class="ion-padding">
      <div class="ion-margin-top">
        <ion-label>Click the handle above to advance to the next breakpoint.</ion-label>
      </div>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');

  modal.breakpoints = [0, 0.25, 0.5, 0.75];
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal
    trigger="open-modal"
    [initialBreakpoint]="0.25"
    [breakpoints]="[0, 0.25, 0.5, 0.75]"
    handleBehavior="cycle"
  >
    <ng-template>
      <ion-content class="ion-padding">
        <div class="ion-margin-top">
          <ion-label>Click the handle above to advance to the next breakpoint.</ion-label>
        </div>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage, IonLabel } from '@ionic/react';

function Example() {
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Sheet Modal
        </IonButton>
        <IonModal
          trigger="open-modal"
          initialBreakpoint={0.25}
          breakpoints={[0, 0.25, 0.5, 0.75]}
          handleBehavior="cycle"
        >
          <IonContent className="ion-padding">
            <div className="ion-margin-top">
              <IonLabel>Click the handle above to advance to the next breakpoint.</IonLabel>
            </div>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

    <ion-modal
      trigger="open-modal"
      :initial-breakpoint="0.25"
      :breakpoints="[0, 0.25, 0.5, 0.75]"
      handle-behavior="cycle"
    >
      <ion-content class="ion-padding">
        <div class="ion-margin-top">
          <ion-label>Click the handle above to advance to the next breakpoint.</ion-label>
        </div>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonLabel } from '@ionic/vue';
</script>
```

### Scrolling content at all breakpoints

Sheet modals can be configured to allow scrolling content at all breakpoints, making them ideal for displaying content larger than the viewport. By setting the `expandToScroll` property to `false`, the content remains scrollable at every breakpoint. Otherwise, by default, scrolling is only enabled when the sheet modal is fully expanded.

**JavaScript**

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal trigger="open-modal" initial-breakpoint="0.25" expand-to-scroll="false">
    <ion-content>
      <ion-list>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Connor Smith</h2>
            <p>Sales Rep</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=m" alt="Portrait of Jack Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Jack Smith</h2>
            <p>Product Designer</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Daniel Smith</h2>
            <p>Product Designer</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=f" alt="Portrait of Claire Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Claire Smith</h2>
            <p>Graphic Designer</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=c" alt="Portrait of Kim Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Kim Smith</h2>
            <p>Software Engineer</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=e" alt="Portrait of Alex Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Alex Smith</h2>
            <p>Software Engineer</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=o" alt="Portrait of Eric Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Eric Smith</h2>
            <p>Product Manager</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=x" alt="Portrait of Grace Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Grace Smith</h2>
            <p>Product Manager</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=q" alt="Portrait of Henry Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Henry Smith</h2>
            <p>Product Owner</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Greg Smith</h2>
            <p>Director of Operations</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Zoey Smith</h2>
            <p>CEO</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=p" alt="Portrait of Oliver Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Oliver Smith</h2>
            <p>COO</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=r" alt="Portrait of Emma Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Emma Smith</h2>
            <p>CTO</p>
          </ion-label>
        </ion-item>
      </ion-list>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');

  modal.breakpoints = [0, 0.25, 0.5, 0.75, 1];
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal
    trigger="open-modal"
    [initialBreakpoint]="0.25"
    [breakpoints]="[0, 0.25, 0.5, 0.75, 1]"
    [expandToScroll]="false"
  >
    <ng-template>
      <ion-content>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=m" alt="Portrait of Jack Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Jack Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=f" alt="Portrait of Claire Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Claire Smith</h2>
              <p>Graphic Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=c" alt="Portrait of Kim Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Kim Smith</h2>
              <p>Software Engineer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=e" alt="Portrait of Alex Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Alex Smith</h2>
              <p>Software Engineer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=o" alt="Portrait of Eric Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Eric Smith</h2>
              <p>Product Manager</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=x" alt="Portrait of Grace Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Grace Smith</h2>
              <p>Product Manager</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=q" alt="Portrait of Henry Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Henry Smith</h2>
              <p>Product Owner</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=p" alt="Portrait of Oliver Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Oliver Smith</h2>
              <p>COO</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=r" alt="Portrait of Emma Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Emma Smith</h2>
              <p>CTO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  IonAvatar,
  IonButton,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonAvatar, IonButton, IonContent, IonHeader, IonItem, IonLabel, IonList, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import {
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonList,
  IonItem,
  IonLabel,
  IonAvatar,
} from '@ionic/react';

function Example() {
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Sheet Modal
        </IonButton>
        <IonModal
          trigger="open-modal"
          initialBreakpoint={0.25}
          breakpoints={[0, 0.25, 0.5, 0.75, 1]}
          expandToScroll={false}
        >
          <IonContent className="ion-padding">
            <IonList>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Connor Smith</h2>
                  <p>Sales Rep</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=m" alt="Portrait of Jack Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Jack Smith</h2>
                  <p>Product Designer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Daniel Smith</h2>
                  <p>Product Designer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=f" alt="Portrait of Claire Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Claire Smith</h2>
                  <p>Graphic Designer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=c" alt="Portrait of Kim Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Kim Smith</h2>
                  <p>Software Engineer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=e" alt="Portrait of Alex Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Alex Smith</h2>
                  <p>Software Engineer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=o" alt="Portrait of Eric Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Eric Smith</h2>
                  <p>Product Manager</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=x" alt="Portrait of Grace Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Grace Smith</h2>
                  <p>Product Manager</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=q" alt="Portrait of Henry Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Henry Smith</h2>
                  <p>Product Owner</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Greg Smith</h2>
                  <p>Director of Operations</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Zoey Smith</h2>
                  <p>CEO</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=p" alt="Portrait of Oliver Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Oliver Smith</h2>
                  <p>COO</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=r" alt="Portrait of Emma Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Emma Smith</h2>
                  <p>CTO</p>
                </IonLabel>
              </IonItem>
            </IonList>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

    <ion-modal
      trigger="open-modal"
      :initial-breakpoint="0.25"
      :breakpoints="[0, 0.25, 0.5, 0.75, 1]"
      :expand-to-scroll="false"
    >
      <ion-content class="ion-padding">
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=m" alt="Portrait of Jack Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Jack Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=f" alt="Portrait of Claire Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Claire Smith</h2>
              <p>Graphic Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=c" alt="Portrait of Kim Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Kim Smith</h2>
              <p>Software Engineer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=e" alt="Portrait of Alex Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Alex Smith</h2>
              <p>Software Engineer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=o" alt="Portrait of Eric Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Eric Smith</h2>
              <p>Product Manager</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=x" alt="Portrait of Grace Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Grace Smith</h2>
              <p>Product Manager</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=q" alt="Portrait of Henry Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Henry Smith</h2>
              <p>Product Owner</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=p" alt="Portrait of Oliver Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Oliver Smith</h2>
              <p>COO</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=r" alt="Portrait of Emma Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Emma Smith</h2>
              <p>CTO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import {
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonItem,
    IonList,
    IonAvatar,
    IonLabel,
  } from '@ionic/vue';
</script>
```

## Theming

[Controller modals](#controller-modals) are appended to the root of your application, outside of the component that presented them. Component-scoped styles will not reach these modals, so their styles must be applied globally, from a stylesheet that is not scoped to a component.

[Inline modals](#inline-modals-recommended) stay where they are declared in your template, so component-scoped styles do apply to them.

### Styling

Modals expose [CSS Custom Properties](#css-custom-properties) and [CSS Shadow Parts](#css-shadow-parts) for customizing their appearance.

**JavaScript**

```html
<style>
  ion-modal {
    --height: 50%;
    --border-radius: 16px;
    --box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  }

  ion-modal::part(backdrop) {
    background: rgba(209, 213, 219);
    opacity: 1;
  }

  ion-modal ion-toolbar {
    --background: rgb(14 116 144);
    --color: white;
  }
</style>

<ion-app>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Modal</ion-button>

    <ion-modal trigger="open-modal">
      <ion-content>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button color="light" onclick="modal.dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ion-modal>
  </ion-content>
</ion-app>

<script>
  var modal = document.querySelector('ion-modal');

  function dismiss() {
    modal.dismiss();
  }
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Modal</ion-button>

  <ion-modal #modal trigger="open-modal">
    <ng-template>
      <ion-content>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button color="light" (click)="modal.dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  IonAvatar,
  IonButton,
  IonButtons,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [
    IonAvatar,
    IonButton,
    IonButtons,
    IonContent,
    IonHeader,
    IonItem,
    IonLabel,
    IonList,
    IonModal,
    IonTitle,
    IonToolbar,
  ],
})
export class ExampleComponent {}
```

`src/global.css`

```css
ion-modal {
  --height: 50%;
  --border-radius: 16px;
  --box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}

ion-modal::part(backdrop) {
  background: rgba(209, 213, 219);
  opacity: 1;
}

ion-modal ion-toolbar {
  --background: rgb(14 116 144);
  --color: white;
}
```

**React**

`src/main.tsx`

```tsx
import React, { useRef } from 'react';
import {
  IonButtons,
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonList,
  IonItem,
  IonLabel,
  IonAvatar,
} from '@ionic/react';

import './main.css';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);

  function dismiss() {
    modal.current?.dismiss();
  }

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Modal
        </IonButton>
        <IonModal id="example-modal" ref={modal} trigger="open-modal">
          <IonContent>
            <IonToolbar>
              <IonTitle>Modal</IonTitle>
              <IonButtons slot="end">
                <IonButton color="light" onClick={() => dismiss()}>
                  Close
                </IonButton>
              </IonButtons>
            </IonToolbar>
            <IonList>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Connor Smith</h2>
                  <p>Sales Rep</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Daniel Smith</h2>
                  <p>Product Designer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Greg Smith</h2>
                  <p>Director of Operations</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Zoey Smith</h2>
                  <p>CEO</p>
                </IonLabel>
              </IonItem>
            </IonList>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

`src/main.css`

```css
ion-modal#example-modal {
  --height: 50%;
  --border-radius: 16px;
  --box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}

ion-modal#example-modal::part(backdrop) {
  background: rgba(209, 213, 219);
  opacity: 1;
}

ion-modal#example-modal ion-toolbar {
  --background: rgb(14 116 144);
  --color: white;
}
```

**Vue**

```html
<style>
  ion-modal {
    --height: 50%;
    --border-radius: 16px;
    --box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  }

  ion-modal::part(backdrop) {
    background: rgba(209, 213, 219);
    opacity: 1;
  }

  ion-modal ion-toolbar {
    --background: rgb(14 116 144);
    --color: white;
  }
</style>

<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Modal</ion-button>

    <ion-modal ref="modal" trigger="open-modal">
      <ion-content>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button color="light" @click="dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import {
    IonButtons,
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonItem,
    IonList,
    IonAvatar,
    IonLabel,
  } from '@ionic/vue';
  import { ref } from 'vue';

  const modal = ref();

  const dismiss = () => modal.value.$el.dismiss();
</script>
```

### Stacked Modals

`ion-modal` works under the assumption that stacked modals are the same size. As a result, each subsequent modal will have no box shadow and a backdrop opacity of `0`. This is to avoid the effect of shadows and backdrops getting darker with each added modal. This can be changed by setting the `--box-shadow` and `--backdrop-opacity` CSS variables:

```css
ion-modal.stack-modal {
  --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
  --backdrop-opacity: var(--ion-backdrop-opacity, 0.32);
}
```

### Sizing to Content

A modal fills the height of its container by default. Setting the `--height` CSS variable to `auto`, `fit-content`, `min-content`, or `max-content` sizes the modal to the height of what it renders instead.

**Warning**

The height must be set on the modal through the `--height` CSS variable. Setting `height` on the `content`[CSS Shadow Part](#css-shadow-parts) will still resize the modal, but any [Content](/docs/api/content.md) inside of it will collapse to a height of `0`, leaving only the header visible.
```css
/* DO NOT DO THIS */
ion-modal::part(content) {
  height: fit-content;
}

/* Do this instead */
ion-modal {
  --height: fit-content;
}
```

**JavaScript**

```html
<style>
  ion-modal {
    --height: fit-content;
    --width: 90%;
    --border-radius: 8px;
    --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
  }
</style>

<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Modal</ion-button>

  <ion-modal trigger="open-modal">
    <ion-header>
      <ion-toolbar>
        <ion-title>Modal</ion-title>
        <ion-buttons slot="end">
          <ion-button onclick="modal.dismiss()">Close</ion-button>
        </ion-buttons>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <ion-list lines="full">
        <ion-item>
          <ion-label>Item 1</ion-label>
        </ion-item>
        <ion-item>
          <ion-label>Item 2</ion-label>
        </ion-item>
        <ion-item>
          <ion-label>Item 3</ion-label>
        </ion-item>
      </ion-list>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Modal</ion-button>

  <ion-modal #modal trigger="open-modal">
    <ng-template>
      <ion-header>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button (click)="modal.dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content>
        <ion-list lines="full">
          @for (item of ['Item 1', 'Item 2', 'Item 3']; track item) {
          <ion-item>
            <ion-label>{{ item }}</ion-label>
          </ion-item>
          }
        </ion-list>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.css`

```css
ion-modal {
  --height: fit-content;
  --width: 90%;
  --border-radius: 8px;
  --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
}
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  IonButton,
  IonButtons,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonButtons, IonContent, IonHeader, IonItem, IonLabel, IonList, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {}
```

**React**

`src/main.tsx`

```tsx
import React, { useRef } from 'react';
import {
  IonButton,
  IonButtons,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonPage,
  IonTitle,
  IonToolbar,
} from '@ionic/react';

import './main.css';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);

  function dismiss() {
    modal.current?.dismiss();
  }

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Modal
        </IonButton>

        <IonModal ref={modal} className="content-sized" trigger="open-modal">
          <IonHeader>
            <IonToolbar>
              <IonTitle>Modal</IonTitle>
              <IonButtons slot="end">
                <IonButton onClick={() => dismiss()}>Close</IonButton>
              </IonButtons>
            </IonToolbar>
          </IonHeader>
          <IonContent>
            <IonList lines="full">
              {['Item 1', 'Item 2', 'Item 3'].map((item) => (
                <IonItem key={item}>
                  <IonLabel>{item}</IonLabel>
                </IonItem>
              ))}
            </IonList>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

`src/main.css`

```css
ion-modal {
  --height: fit-content;
  --width: 90%;
  --border-radius: 8px;
  --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
}
```

**Vue**

```vue
<style>
ion-modal {
  --height: fit-content;
  --width: 90%;
  --border-radius: 8px;
  --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
}
</style>

<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Modal</ion-button>

    <ion-modal ref="modal" trigger="open-modal">
      <ion-header>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button @click="dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content>
        <ion-list lines="full">
          <ion-item v-for="item in ['Item 1', 'Item 2', 'Item 3']" :key="item">
            <ion-label>{{ item }}</ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
import {
  IonButton,
  IonButtons,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/vue';
import { ref } from 'vue';

const modal = ref();

const dismiss = () => modal.value.$el.dismiss();
</script>
```

## Animations

The enter and leave animations can be customized by using our animation builder and assigning animations to `enterAnimation` and `leaveAnimation`.

**JavaScript**

`index.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Modal</ion-button>

  <ion-modal trigger="open-modal">
    <ion-header>
      <ion-toolbar>
        <ion-title>Modal</ion-title>
        <ion-buttons slot="end">
          <ion-button onclick="modal.dismiss()">Close</ion-button>
        </ion-buttons>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <ion-list>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Connor Smith</h2>
            <p>Sales Rep</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Daniel Smith</h2>
            <p>Product Designer</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Greg Smith</h2>
            <p>Director of Operations</p>
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-avatar slot="start">
            <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
          </ion-avatar>
          <ion-label>
            <h2>Zoey Smith</h2>
            <p>CEO</p>
          </ion-label>
        </ion-item>
      </ion-list>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');

  const enterAnimation = (baseEl) => {
    const root = baseEl.shadowRoot;

    const backdropAnimation = createAnimation()
      .addElement(root.querySelector('ion-backdrop'))
      .fromTo('opacity', '0.01', 'var(--backdrop-opacity)');

    const wrapperAnimation = createAnimation()
      .addElement(root.querySelector('.modal-wrapper'))
      .keyframes([
        { offset: 0, opacity: '0', transform: 'scale(0)' },
        { offset: 1, opacity: '0.99', transform: 'scale(1)' },
      ]);

    return createAnimation()
      .addElement(baseEl)
      .easing('ease-out')
      .duration(500)
      .addAnimation([backdropAnimation, wrapperAnimation]);
  };

  const leaveAnimation = (baseEl) => enterAnimation(baseEl).direction('reverse');

  modal.enterAnimation = enterAnimation;
  modal.leaveAnimation = leaveAnimation;

  function dismiss() {
    modal.dismiss();
  }
</script>
```

`index.ts`

```ts
import { defineCustomElements } from '@ionic/core/loader';

import { createAnimation } from '@ionic/core';

/* Core CSS required for Ionic components to work properly */
import '@ionic/core/css/core.css';

/* Basic CSS for apps built with Ionic */
import '@ionic/core/css/normalize.css';
import '@ionic/core/css/structure.css';
import '@ionic/core/css/typography.css';

/* Optional CSS utils that can be commented out */
import '@ionic/core/css/padding.css';
import '@ionic/core/css/float-elements.css';
import '@ionic/core/css/text-alignment.css';
import '@ionic/core/css/text-transformation.css';
import '@ionic/core/css/flex-utils.css';
import '@ionic/core/css/display.css';

/**
 * Ionic Dark Palette
 * -----------------------------------------------------
 * For more information, please see:
 * https://ionicframework.com/docs/theming/dark-mode
 */

// import '@ionic/core/css/palettes/dark.always.css';
// import '@ionic/core/css/palettes/dark.class.css';
import '@ionic/core/css/palettes/dark.system.css';

/* Theme variables */
import './theme/variables.css';

defineCustomElements();

(window as any).createAnimation = createAnimation;
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Modal</ion-button>

  <ion-modal #modal trigger="open-modal" [enterAnimation]="enterAnimation" [leaveAnimation]="leaveAnimation">
    <ng-template>
      <ion-header>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button (click)="modal.dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  AnimationController,
  IonAvatar,
  IonButton,
  IonButtons,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [
    IonAvatar,
    IonButton,
    IonButtons,
    IonContent,
    IonHeader,
    IonItem,
    IonLabel,
    IonList,
    IonModal,
    IonTitle,
    IonToolbar,
  ],
})
export class ExampleComponent {
  constructor(private animationCtrl: AnimationController) {}

  enterAnimation = (baseEl: HTMLElement) => {
    const root = baseEl.shadowRoot;

    const backdropAnimation = this.animationCtrl
      .create()
      .addElement(root!.querySelector('ion-backdrop')!)
      .fromTo('opacity', '0.01', 'var(--backdrop-opacity)');

    const wrapperAnimation = this.animationCtrl
      .create()
      .addElement(root!.querySelector('.modal-wrapper')!)
      .keyframes([
        { offset: 0, opacity: '0', transform: 'scale(0)' },
        { offset: 1, opacity: '0.99', transform: 'scale(1)' },
      ]);

    return this.animationCtrl
      .create()
      .addElement(baseEl)
      .easing('ease-out')
      .duration(500)
      .addAnimation([backdropAnimation, wrapperAnimation]);
  };

  leaveAnimation = (baseEl: HTMLElement) => {
    return this.enterAnimation(baseEl).direction('reverse');
  };
}
```

**React**

```tsx
import React, { useRef } from 'react';
import {
  createAnimation,
  IonButtons,
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonList,
  IonItem,
  IonLabel,
  IonAvatar,
} from '@ionic/react';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);

  function dismiss() {
    modal.current?.dismiss();
  }

  const enterAnimation = (baseEl: HTMLElement) => {
    const root = baseEl.shadowRoot;

    const backdropAnimation = createAnimation()
      .addElement(root?.querySelector('ion-backdrop')!)
      .fromTo('opacity', '0.01', 'var(--backdrop-opacity)');

    const wrapperAnimation = createAnimation()
      .addElement(root?.querySelector('.modal-wrapper')!)
      .keyframes([
        { offset: 0, opacity: '0', transform: 'scale(0)' },
        { offset: 1, opacity: '0.99', transform: 'scale(1)' },
      ]);

    return createAnimation()
      .addElement(baseEl)
      .easing('ease-out')
      .duration(500)
      .addAnimation([backdropAnimation, wrapperAnimation]);
  };

  const leaveAnimation = (baseEl: HTMLElement) => {
    return enterAnimation(baseEl).direction('reverse');
  };

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Modal
        </IonButton>
        <IonModal
          id="example-modal"
          ref={modal}
          trigger="open-modal"
          enterAnimation={enterAnimation}
          leaveAnimation={leaveAnimation}
        >
          <IonContent>
            <IonToolbar>
              <IonTitle>Modal</IonTitle>
              <IonButtons slot="end">
                <IonButton onClick={() => dismiss()}>Close</IonButton>
              </IonButtons>
            </IonToolbar>
            <IonList>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Connor Smith</h2>
                  <p>Sales Rep</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Daniel Smith</h2>
                  <p>Product Designer</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Greg Smith</h2>
                  <p>Director of Operations</p>
                </IonLabel>
              </IonItem>
              <IonItem>
                <IonAvatar slot="start">
                  <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
                </IonAvatar>
                <IonLabel>
                  <h2>Zoey Smith</h2>
                  <p>CEO</p>
                </IonLabel>
              </IonItem>
            </IonList>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Modal</ion-button>

    <ion-modal ref="modal" trigger="open-modal" :enter-animation="enterAnimation" :leave-animation="leaveAnimation">
      <ion-content>
        <ion-toolbar>
          <ion-title>Modal</ion-title>
          <ion-buttons slot="end">
            <ion-button @click="dismiss()">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
        <ion-list>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=b" alt="Portrait of Connor Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Connor Smith</h2>
              <p>Sales Rep</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=a" alt="Portrait of Daniel Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Daniel Smith</h2>
              <p>Product Designer</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=d" alt="Portrait of Greg Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Greg Smith</h2>
              <p>Director of Operations</p>
            </ion-label>
          </ion-item>
          <ion-item>
            <ion-avatar slot="start">
              <img src="https://i.pravatar.cc/300?u=l" alt="Portrait of Zoey Smith" />
            </ion-avatar>
            <ion-label>
              <h2>Zoey Smith</h2>
              <p>CEO</p>
            </ion-label>
          </ion-item>
        </ion-list>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import {
    createAnimation,
    IonButtons,
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonItem,
    IonList,
    IonAvatar,
    IonLabel,
  } from '@ionic/vue';
  import { ref } from 'vue';

  const modal = ref();

  const dismiss = () => modal.value.$el.dismiss();

  const enterAnimation = (baseEl: HTMLElement) => {
    const root = baseEl.shadowRoot;

    const backdropAnimation = createAnimation()
      .addElement(root.querySelector('ion-backdrop'))
      .fromTo('opacity', '0.01', 'var(--backdrop-opacity)');

    const wrapperAnimation = createAnimation()
      .addElement(root.querySelector('.modal-wrapper'))
      .keyframes([
        { offset: 0, opacity: '0', transform: 'scale(0)' },
        { offset: 1, opacity: '0.99', transform: 'scale(1)' },
      ]);

    return createAnimation()
      .addElement(baseEl)
      .easing('ease-out')
      .duration(500)
      .addAnimation([backdropAnimation, wrapperAnimation]);
  };

  const leaveAnimation = (baseEl) => {
    return enterAnimation(baseEl).direction('reverse');
  };
</script>
```

## Custom Dialogs

While `ion-modal` is most often used for full-page views, cards, or sheets, it is also possible to use it for custom dialogs. This is useful if developers need an interface that is more complex than what components such as [ion-alert](/docs/api/alert.md) or [ion-loading](/docs/api/loading.md) provide.

**JavaScript**

`index.html`

```html
<style>
  ion-modal#example-modal {
    --width: fit-content;
    --min-width: 250px;
    --height: fit-content;
    --border-radius: 6px;
    --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
  }

  ion-modal#example-modal h1 {
    margin: 20px 20px 10px 20px;
  }

  ion-modal#example-modal ion-icon {
    margin-right: 6px;

    width: 48px;
    height: 48px;

    padding: 4px 0;

    color: #aaaaaa;
  }

  ion-modal#example-modal .wrapper {
    margin-bottom: 10px;
  }
</style>

<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-custom-dialog" expand="block">Open Custom Dialog</ion-button>

  <ion-modal id="example-modal" trigger="open-custom-dialog">
    <div class="wrapper">
      <h1>Dialog header</h1>

      <ion-list lines="none">
        <ion-item button="true" detail="false" onclick="dismiss()">
          <ion-icon name="person-circle"></ion-icon>
          <ion-label>Item 1</ion-label>
        </ion-item>
        <ion-item button="true" detail="false" onclick="dismiss()">
          <ion-icon name="person-circle"></ion-icon>
          <ion-label>Item 2</ion-label>
        </ion-item>
        <ion-item button="true" detail="false" onclick="dismiss()">
          <ion-icon name="person-circle"></ion-icon>
          <ion-label>Item 3</ion-label>
        </ion-item>
      </ion-list>
    </div>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');
  function dismiss() {
    modal.dismiss();
  }
</script>
```

`index.ts`

```ts
import { defineCustomElements } from '@ionic/core/loader';

import { addIcons } from 'ionicons';
import { personCircle } from 'ionicons/icons';

/* Core CSS required for Ionic components to work properly */
import '@ionic/core/css/core.css';

/* Basic CSS for apps built with Ionic */
import '@ionic/core/css/normalize.css';
import '@ionic/core/css/structure.css';
import '@ionic/core/css/typography.css';

/* Optional CSS utils that can be commented out */
import '@ionic/core/css/padding.css';
import '@ionic/core/css/float-elements.css';
import '@ionic/core/css/text-alignment.css';
import '@ionic/core/css/text-transformation.css';
import '@ionic/core/css/flex-utils.css';
import '@ionic/core/css/display.css';

/**
 * Ionic Dark Palette
 * -----------------------------------------------------
 * For more information, please see:
 * https://ionicframework.com/docs/theming/dark-mode
 */

// import '@ionic/core/css/palettes/dark.always.css';
// import '@ionic/core/css/palettes/dark.class.css';
import '@ionic/core/css/palettes/dark.system.css';

/* Theme variables */
import './theme/variables.css';

/**
 * On Ionicons 7.2+ this icon
 * gets mapped to a "person-circle" key.
 * Alternatively, developers can do:
 * addIcons({ 'person-circle': personCircle });
 */
addIcons({ personCircle });

defineCustomElements();
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-custom-dialog" expand="block">Open Custom Dialog</ion-button>

  <ion-modal id="example-modal" #modal trigger="open-custom-dialog">
    <ng-template>
      <div class="wrapper">
        <h1>Dialog header</h1>

        <ion-list lines="none">
          <ion-item button="true" detail="false" (click)="modal.dismiss()">
            <ion-icon name="person-circle"></ion-icon>
            <ion-label>Item 1</ion-label>
          </ion-item>
          <ion-item button="true" detail="false" (click)="modal.dismiss()">
            <ion-icon name="person-circle"></ion-icon>
            <ion-label>Item 2</ion-label>
          </ion-item>
          <ion-item button="true" detail="false" (click)="modal.dismiss()">
            <ion-icon name="person-circle"></ion-icon>
            <ion-label>Item 3</ion-label>
          </ion-item>
        </ion-list>
      </div>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import {
  IonButton,
  IonContent,
  IonHeader,
  IonIcon,
  IonItem,
  IonLabel,
  IonList,
  IonModal,
  IonTitle,
  IonToolbar,
} from '@ionic/angular';

import { addIcons } from 'ionicons';
import { personCircle } from 'ionicons/icons';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonContent, IonHeader, IonIcon, IonItem, IonLabel, IonList, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {
  constructor() {
    /**
     * Any icons you want to use in your application
     * can be registered in app.component.ts and then
     * referenced by name anywhere in your application.
     */
    addIcons({ personCircle });
  }
}
```

`src/global.css`

```css
ion-modal#example-modal {
  --width: fit-content;
  --min-width: 250px;
  --height: fit-content;
  --border-radius: 6px;
  --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
}

ion-modal#example-modal h1 {
  margin: 20px 20px 10px 20px;
}

ion-modal#example-modal ion-icon {
  margin-right: 6px;

  width: 48px;
  height: 48px;

  padding: 4px 0;

  color: #aaaaaa;
}

ion-modal#example-modal .wrapper {
  margin-bottom: 10px;
}
```

**React**

`src/main.tsx`

```tsx
import React, { useRef } from 'react';
import {
  IonButton,
  IonModal,
  IonHeader,
  IonContent,
  IonToolbar,
  IonTitle,
  IonPage,
  IonList,
  IonItem,
  IonLabel,
  IonIcon,
} from '@ionic/react';
import { personCircle } from 'ionicons/icons';

import './main.css';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);

  function dismiss() {
    modal.current?.dismiss();
  }

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent class="ion-padding">
        <IonButton id="open-custom-dialog" expand="block">
          Open Custom Dialog
        </IonButton>
        <IonModal id="example-modal" ref={modal} trigger="open-custom-dialog">
          <div className="wrapper">
            <h1>Dialog header</h1>

            <IonList lines="none">
              <IonItem button={true} detail={false} onClick={dismiss}>
                <IonIcon icon={personCircle}></IonIcon>
                <IonLabel>Item 1</IonLabel>
              </IonItem>
              <IonItem button={true} detail={false} onClick={dismiss}>
                <IonIcon icon={personCircle}></IonIcon>
                <IonLabel>Item 2</IonLabel>
              </IonItem>
              <IonItem button={true} detail={false} onClick={dismiss}>
                <IonIcon icon={personCircle}></IonIcon>
                <IonLabel>Item 3</IonLabel>
              </IonItem>
            </IonList>
          </div>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

`src/main.css`

```css
ion-modal#example-modal {
  --width: fit-content;
  --min-width: 250px;
  --height: fit-content;
  --border-radius: 6px;
  --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
}

ion-modal#example-modal h1 {
  margin: 20px 20px 10px 20px;
}

ion-modal#example-modal ion-icon {
  margin-right: 6px;

  width: 48px;
  height: 48px;

  padding: 4px 0;

  color: #aaaaaa;
}

ion-modal#example-modal .wrapper {
  margin-bottom: 10px;
}
```

**Vue**

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-custom-dialog" expand="block">Open Custom Dialog</ion-button>

    <ion-modal id="example-modal" ref="modal" trigger="open-custom-dialog">
      <div class="wrapper">
        <h1>Dialog header</h1>

        <ion-list lines="none">
          <ion-item :button="true" :detail="false" @click="dismiss()">
            <ion-icon :icon="personCircle"></ion-icon>
            <ion-label>Item 1</ion-label>
          </ion-item>
          <ion-item :button="true" :detail="false" @click="dismiss()">
            <ion-icon :icon="personCircle"></ion-icon>
            <ion-label>Item 2</ion-label>
          </ion-item>
          <ion-item :button="true" :detail="false" @click="dismiss()">
            <ion-icon :icon="personCircle"></ion-icon>
            <ion-label>Item 3</ion-label>
          </ion-item>
        </ion-list>
      </div>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import {
    IonButton,
    IonModal,
    IonHeader,
    IonContent,
    IonToolbar,
    IonTitle,
    IonItem,
    IonList,
    IonLabel,
    IonIcon,
  } from '@ionic/vue';
  import { personCircle } from 'ionicons/icons';
  import { ref } from 'vue';

  const modal = ref();

  const dismiss = () => modal.value.$el.dismiss();
</script>
<style>
  ion-modal#example-modal {
    --width: fit-content;
    --min-width: 250px;
    --height: fit-content;
    --border-radius: 6px;
    --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
  }

  ion-modal#example-modal h1 {
    margin: 20px 20px 10px 20px;
  }

  ion-modal#example-modal ion-icon {
    margin-right: 6px;

    width: 48px;
    height: 48px;

    padding: 4px 0;

    color: #aaaaaa;
  }

  ion-modal#example-modal .wrapper {
    margin-bottom: 10px;
  }
</style>
```

A few things to keep in mind when creating custom dialogs:

- `ion-content` can be used in a custom dialog, including one that is sized to its contents. The dialog's height must come from the `--height` CSS variable rather than from the dialog's children. See [Sizing to Content](#sizing-to-content).
- Creating custom dialogs provides a way of ejecting from the default modal experience. As a result, custom dialogs should not be used with card or sheet modals.

## Event Handling

### Using `ionDragStart` and `ionDragEnd`

The `ionDragStart` event is emitted as soon as the user begins a dragging gesture on the modal. This event fires at the moment the user initiates contact with the handle or modal surface, before any actual displacement occurs. It is particularly useful for preparing the interface for a transition, such as hiding certain interactive elements (like headers or buttons) to ensure a smooth dragging experience.

The `ionDragEnd` event is emitted when the user completes the dragging gesture by releasing the modal. Like the move event, it includes the final [`ModalDragEventDetail`](#modaldrageventdetail) object. This event is commonly used to finalize state changes once the modal has come to a rest.

**JavaScript**

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal trigger="open-modal" initial-breakpoint="0.25">
    <ion-content class="ion-padding">
      <div class="ion-margin-top">
        <ion-label>Drag the handle to adjust the header's visibility.</ion-label>
      </div>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  const modal = document.querySelector('ion-modal');
  const header = document.querySelector('ion-header');

  modal.breakpoints = [0, 0.25, 0.5, 0.75, 1];

  modal.addEventListener('ionDragStart', () => {
    console.log('Drag started');

    header.style.opacity = 0;
  });

  modal.addEventListener('ionDragEnd', (event) => {
    console.log('Drag ended');

    header.style.opacity = 1;
  });
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header #header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal
    trigger="open-modal"
    [initialBreakpoint]="0.25"
    [breakpoints]="[0, 0.25, 0.5, 0.75, 1]"
    (ionDragStart)="onDragStart()"
    (ionDragEnd)="onDragEnd($event)"
  >
    <ng-template>
      <ion-content class="ion-padding">
        <div class="ion-margin-top">
          <ion-label>Drag the handle to adjust the header's visibility.</ion-label>
        </div>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component, ElementRef, ViewChild } from '@angular/core';
import { IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar } from '@ionic/angular/standalone';
import type { ModalDragEventDetail } from '@ionic/angular/standalone';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  imports: [IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {
  @ViewChild('header', { read: ElementRef })
  header!: ElementRef<HTMLIonHeaderElement>;

  onDragStart() {
    console.log('Drag started');

    const headerEl = this.header.nativeElement;

    headerEl.style.opacity = '0';
  }

  onDragEnd(event: CustomEvent<ModalDragEventDetail>) {
    console.log('Drag ended');

    const headerEl = this.header.nativeElement;

    headerEl.style.opacity = '1';
  }
}
```

**React**

```tsx
import React, { useRef } from 'react';
import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage, IonLabel } from '@ionic/react';
import type { ModalDragEventDetail } from '@ionic/react';

function Example() {
  const header = useRef<HTMLIonHeaderElement>(null);

  const onDragStart = () => {
    console.log('Drag started');

    const headerEl = header.current!;

    headerEl.style.opacity = '0';
  };

  const onDragEnd = (event: CustomEvent<ModalDragEventDetail>) => {
    console.log('Drag ended');

    const headerEl = header.current!;

    headerEl.style.opacity = '1';
  };

  return (
    <IonPage>
      <IonHeader ref={header}>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Sheet Modal
        </IonButton>
        <IonModal
          trigger="open-modal"
          initialBreakpoint={0.25}
          breakpoints={[0, 0.25, 0.5, 0.75, 1]}
          onIonDragStart={onDragStart}
          onIonDragEnd={onDragEnd}
        >
          <IonContent className="ion-padding">
            <div className="ion-margin-top">
              <IonLabel>Drag the handle to adjust the header's visibility.</IonLabel>
            </div>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```vue
<template>
  <ion-header ref="header">
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

    <ion-modal
      ref="modal"
      trigger="open-modal"
      :initial-breakpoint="0.25"
      :breakpoints="[0, 0.25, 0.5, 0.75, 1]"
      @ionDragStart="onDragStart()"
      @ionDragEnd="onDragEnd($event)"
    >
      <ion-content class="ion-padding">
        <div class="ion-margin-top">
          <ion-label>Drag the handle to adjust the header's visibility.</ion-label>
        </div>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonLabel } from '@ionic/vue';
import type { ModalDragEventDetail } from '@ionic/vue';

const header = ref<InstanceType<typeof IonHeader>>();

const onDragStart = () => {
  console.log('Drag started');

  const headerEl = header.value!.$el;

  headerEl.style.opacity = '0';
};

const onDragEnd = (event: CustomEvent<ModalDragEventDetail>) => {
  console.log('Drag ended');

  const headerEl = header.value!.$el;

  headerEl.style.opacity = '1';
};
</script>
```

`Console`
`Console messages will appear here when logged from the example above.`

### Using `ionDragMove`

The `ionDragMove` event is emitted continuously while the user is actively dragging the modal. This event provides a [`ModalDragEventDetail`](#modaldrageventdetail) object containing real-time data, essential for creating highly responsive UI updates that react instantly to the user's touch. For example, the `progress` value can be used to dynamically darken a header's opacity as the modal is dragged upward.

**JavaScript**

```html
<ion-header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal trigger="open-modal" initial-breakpoint="0.25">
    <ion-content class="ion-padding">
      <div class="ion-margin-top">
        <ion-label>Drag the handle to adjust the header's visibility.</ion-label>
      </div>
    </ion-content>
  </ion-modal>
</ion-content>

<script>
  const modal = document.querySelector('ion-modal');
  const header = document.querySelector('ion-header');
  // Assign the current snap breakpoint to the initial breakpoint so
  // that we can track changes during the drag
  let currentSnap = 0.25;
  modal.breakpoints = [0, 0.25, 0.5, 0.75, 1];

  modal.addEventListener('ionDragMove', (event) => {
    // `progress` is a value from 1 (top) to 0 (bottom)
    // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends
    const { progress, snapBreakpoint } = event.detail;

    if (currentSnap !== snapBreakpoint) {
      currentSnap = snapBreakpoint;

      console.log('Current snap breakpoint:', snapBreakpoint);
    }

    /**
     * Inverse relationship:
     * 1.0 progress = 0 opacity
     * 0 progress = 1.0 opacity
     */
    const currentOpacity = 1 - progress;

    header.style.opacity = currentOpacity;
  });

  modal.addEventListener('ionDragEnd', (event) => {
    // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends
    const { progress, snapBreakpoint } = event.detail;

    /**
     * If the modal is snapping to the closed state (0), reset the
     * styles.
     */
    if (snapBreakpoint === 0) {
      header.style.removeProperty('opacity');
      return;
    }

    // Smooth transition to the final resting opacity
    header.style.transition = 'opacity 0.4s ease';
    // The final opacity matches the inverse of the resting progress
    header.style.opacity = 1 - progress;
  });

  /**
   * If the user dismisses the modal (e.g. tapping the backdrop),
   * reset the styles.
   */
  modal.addEventListener('willDismiss', (event) => {
    // Reset styles when the modal is dismissed
    header.style.removeProperty('opacity');
    header.style.removeProperty('transition');
  });
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header #header>
  <ion-toolbar>
    <ion-title>App</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

  <ion-modal
    trigger="open-modal"
    [initialBreakpoint]="0.25"
    [breakpoints]="[0, 0.25, 0.5, 0.75, 1]"
    (ionDragMove)="onDragMove($event)"
    (ionDragEnd)="onDragEnd($event)"
    (willDismiss)="onWillDismiss()"
  >
    <ng-template>
      <ion-content class="ion-padding">
        <div class="ion-margin-top">
          <ion-label>Drag the handle to adjust the header's visibility.</ion-label>
        </div>
      </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component, ElementRef, ViewChild } from '@angular/core';
import { IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar } from '@ionic/angular/standalone';
import type { ModalDragEventDetail } from '@ionic/angular/standalone';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  imports: [IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {
  @ViewChild('header', { read: ElementRef })
  header!: ElementRef<HTMLIonHeaderElement>;
  // Assign the current snap breakpoint to the initial breakpoint so
  // that we can track changes during the drag
  currentSnap = 0.25;

  onDragMove(event: CustomEvent<ModalDragEventDetail>) {
    // `progress` is a value from 1 (top) to 0 (bottom)
    // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends
    const { progress, snapBreakpoint } = event.detail;

    if (this.currentSnap !== snapBreakpoint) {
      this.currentSnap = snapBreakpoint as number;

      console.log('Current snap breakpoint:', snapBreakpoint);
    }

    const headerEl = this.header.nativeElement;
    /**
     * Inverse relationship:
     * 1.0 progress = 0 opacity
     * 0 progress = 1.0 opacity
     */
    const currentOpacity = 1 - progress;

    headerEl.style.opacity = currentOpacity.toString();
  }

  onDragEnd(event: CustomEvent<ModalDragEventDetail>) {
    // `progress` is a value from 1 (top) to 0 (bottom)
    // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends
    const { progress, snapBreakpoint } = event.detail;
    const headerEl = this.header.nativeElement;

    /**
     * If the modal is snapping to the closed state (0), reset the
     * styles.
     */
    if (snapBreakpoint === 0) {
      headerEl.style.removeProperty('opacity');
      headerEl.style.removeProperty('transition');
      return;
    }

    // Smooth transition to the final resting opacity
    headerEl.style.transition = 'opacity 0.4s ease';
    // The final opacity matches the inverse of the resting progress
    headerEl.style.opacity = (1 - progress).toString();
  }

  /**
   * If the user dismisses the modal (e.g. tapping the backdrop),
   * reset the styles.
   */
  onWillDismiss() {
    const headerEl = this.header.nativeElement;

    // Reset styles when the modal is dismissed
    headerEl.style.removeProperty('opacity');
    headerEl.style.removeProperty('transition');
  }
}
```

**React**

```tsx
import React, { useRef } from 'react';
import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage, IonLabel } from '@ionic/react';
import type { ModalDragEventDetail } from '@ionic/react';

function Example() {
  const header = useRef<HTMLIonHeaderElement>(null);
  // Assign the current snap breakpoint to the initial breakpoint so
  // that we can track changes during the drag
  const currentSnap = useRef(0.25);

  const onDragMove = (event: CustomEvent<ModalDragEventDetail>) => {
    // `progress` is a value from 1 (top) to 0 (bottom)
    // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends
    const { progress, snapBreakpoint } = event.detail;
    const headerEl = header.current!;

    if (currentSnap.current !== snapBreakpoint) {
      currentSnap.current = snapBreakpoint as number;

      console.log('Current snap breakpoint:', snapBreakpoint);
    }

    /**
     * Inverse relationship:
     * 1.0 progress = 0 opacity
     * 0 progress = 1.0 opacity
     */
    const currentOpacity = 1 - progress;

    headerEl.style.opacity = currentOpacity.toString();
  };

  const onDragEnd = (event: CustomEvent<ModalDragEventDetail>) => {
    // `progress` is a value from 1 (top) to 0 (bottom)
    // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends
    const { progress, snapBreakpoint } = event.detail;
    const headerEl = header.current!;

    /**
     * If the modal is snapping to the closed state (0), reset the
     * styles.
     */
    if (snapBreakpoint === 0) {
      headerEl.style.removeProperty('opacity');
      headerEl.style.removeProperty('transition');
      return;
    }

    // Smooth transition to the final resting opacity
    headerEl.style.transition = 'opacity 0.4s ease';
    // The final opacity matches the inverse of the resting progress
    headerEl.style.opacity = (1 - progress).toString();
  };

  /**
   * If the user dismisses the modal (e.g. tapping the backdrop),
   * reset the styles.
   */
  const onWillDismiss = () => {
    const headerEl = header.current!;

    // Reset styles when the modal is dismissed
    headerEl.style.removeProperty('opacity');
    headerEl.style.removeProperty('transition');
  };

  return (
    <IonPage>
      <IonHeader ref={header}>
        <IonToolbar>
          <IonTitle>App</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Sheet Modal
        </IonButton>
        <IonModal
          trigger="open-modal"
          initialBreakpoint={0.25}
          breakpoints={[0, 0.25, 0.5, 0.75, 1]}
          onIonDragMove={onDragMove}
          onIonDragEnd={onDragEnd}
          onWillDismiss={onWillDismiss}
        >
          <IonContent className="ion-padding">
            <div className="ion-margin-top">
              <IonLabel>Drag the handle to adjust the header's visibility.</IonLabel>
            </div>
          </IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```vue
<template>
  <ion-header ref="header">
    <ion-toolbar>
      <ion-title>App</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>

    <ion-modal
      trigger="open-modal"
      :initial-breakpoint="0.25"
      :breakpoints="[0, 0.25, 0.5, 0.75, 1]"
      @ionDragMove="onDragMove($event)"
      @ionDragEnd="onDragEnd($event)"
      @willDismiss="onWillDismiss()"
    >
      <ion-content class="ion-padding">
        <div class="ion-margin-top">
          <ion-label>Drag the handle to adjust the header's visibility.</ion-label>
        </div>
      </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonLabel } from '@ionic/vue';
import type { ModalDragEventDetail } from '@ionic/vue';

const header = ref<InstanceType<typeof IonHeader>>();
// Assign the current snap breakpoint to the initial breakpoint so
// that we can track changes during the drag
let currentSnap = 0.25;

const onDragMove = (event: CustomEvent<ModalDragEventDetail>) => {
  // `progress` is a value from 1 (top) to 0 (bottom)
  // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends
  const { progress, snapBreakpoint } = event.detail;
  const headerEl = header.value!.$el;

  if (currentSnap !== snapBreakpoint) {
    currentSnap = snapBreakpoint;

    console.log('Current snap breakpoint:', snapBreakpoint);
  }

  /**
   * Inverse relationship:
   * 1.0 progress = 0 opacity
   * 0 progress = 1.0 opacity
   */
  const currentOpacity = 1 - progress;

  headerEl.style.opacity = currentOpacity.toString();
};

const onDragEnd = (event: CustomEvent<ModalDragEventDetail>) => {
  // `progress` is a value from 1 (top) to 0 (bottom)
  // `snapBreakpoint` tells us which snap point the modal will animate to after the drag ends
  const { progress, snapBreakpoint } = event.detail;
  const headerEl = header.value!.$el;

  /**
   * If the modal is snapping to the closed state (0), reset the
   * styles.
   */
  if (snapBreakpoint === 0) {
    headerEl.style.removeProperty('opacity');
    headerEl.style.removeProperty('transition');
    return;
  }

  // Smooth transition to the final resting opacity
  headerEl.style.transition = 'opacity 0.4s ease';
  // The final opacity matches the inverse of the resting progress
  headerEl.style.opacity = (1 - progress).toString();
};

/**
 * If the user dismisses the modal (e.g. tapping the backdrop),
 * reset the styles.
 */
const onWillDismiss = () => {
  const headerEl = header.value!.$el;

  // Reset styles when the modal is dismissed
  headerEl.style.removeProperty('opacity');
  headerEl.style.removeProperty('transition');
};
</script>
```

`Console`
`Console messages will appear here when logged from the example above.`

## Interfaces

### ModalOptions

Below you will find all of the options available to you when using the `modalController`. These options should be supplied when calling `modalController.create()`.

```typescript
interface ModalOptions {
  component: any;
  componentProps?: { [key: string]: any };
  presentingElement?: HTMLElement;
  showBackdrop?: boolean;
  backdropDismiss?: boolean;
  cssClass?: string | string[];
  animated?: boolean;
  canDismiss?: boolean | ((data?: any, role?: string) => Promise<boolean>);

  mode?: 'ios' | 'md';
  keyboardClose?: boolean;
  id?: string;
  htmlAttributes?: { [key: string]: any };

  enterAnimation?: AnimationBuilder;
  leaveAnimation?: AnimationBuilder;

  breakpoints?: number[];
  initialBreakpoint?: number;
  backdropBreakpoint?: number;
  handle?: boolean;
}
```

### ModalCustomEvent

While not required, this interface can be used in place of the `CustomEvent` interface for stronger typing with Ionic events emitted from this component.

```typescript
interface ModalCustomEvent extends CustomEvent {
  target: HTMLIonModalElement;
}
```

### ModalDragEventDetail

When using the `ionDragMove` and `ionDragEnd` events, the event detail contains the following properties:

```typescript
interface ModalDragEventDetail {
  /**
   * The current Y position of the modal.
   *
   * This can be used to determine how far the modal has been dragged.
   */
  currentY: number;
  /**
   * The change in Y position since the gesture started.
   *
   * This can be used to determine the direction of the drag.
   */
  deltaY: number;
  /**
   * The velocity of the drag in the Y direction.
   *
   * This can be used to determine how fast the modal is being dragged.
   */
  velocityY: number;
  /**
   * A number between 0 and 1.
   *
   * In a sheet modal, progress represents the relative position between
   * the lowest and highest defined breakpoints.
   *
   * In a card modal, it measures the relative position between the
   * bottom of the screen and the top of the modal when it is fully
   * open.
   *
   * This can be used to style content based on how far the modal has
   * been dragged.
   */
  progress: number;
  /**
   * If the modal is a sheet modal, this will be the breakpoint that
   * the modal will snap to if the user lets go of the modal at the
   * current moment.
   *
   * If it's a card modal, this property will not be included in the
   * event payload.
   *
   * This can be used to style content based on where the modal will
   * snap to upon release.
   */
  snapBreakpoint?: number;
  /**
   * Whether the modal is attempting to dismiss when the drag gesture
   * ends.
   *
   * In a sheet modal, this is `true` when the modal will snap to a
   * breakpoint of 0. In a card modal, it is `true` when the gesture
   * passed the threshold required to close the modal.
   *
   * If a `canDismiss` callback is set, it can still cancel the dismiss
   * after this event is emitted. Listen for `ionModalDidDismiss` to
   * confirm that the modal closed.
   *
   * This property is only included on `ionDragEnd`.
   *
   * This can be used to react as soon as the user releases the modal,
   * such as dismissing the keyboard while the modal animates away.
   */
  isDismissing?: boolean;
}
```

## Accessibility

### Keyboard Interactions

| Key | Description |
| --- | --- |
| Esc | Dismisses the modal |

### Labels

Modals have a [`dialog`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role) role. As a result, developers **must** properly label their modals. If the modal is using `ion-title`, the text inside can be used to label the modal itself by setting [`aria-labelledby`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-labelledby) on `ion-modal`. If the modal contains additional descriptive text, this text can be associated with the modal by using [`aria-describedby`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby).

### Screen Readers

Modals have the `aria-modal` attribute applied. This attribute can cause assistive technologies to limit navigation to the modal element's contents. As a result, using gestures that move to the next or previous items may not focus elements outside of the modal. This applies even when the backdrop is disabled in sheet modals using the `backdropBreakpoint` property.

Assistive technologies will not limit navigation to the modal element's contents if developers manually move focus. However, manually moving focus outside of a modal is not supported in Ionic for modals that have focus trapping enabled.

See [https://w3c.github.io/aria/#aria-modal](https://w3c.github.io/aria/#aria-modal) for more information.

### Focus Trapping

When a modal is presented, focus will be trapped inside of the presented modal. Users can focus other interactive elements inside the modal but will never be able to focus interactive elements outside the modal while the modal is presented. For applications that present multiple stacked modals, focus will be trapped on the modal that was presented last.

Sheet modals that have had their backdrop disabled by the `backdropBreakpoint` property are not subject to focus trapping.

### Sheet Modals

Sheet modals allow users to interact with content behind the modal when the `backdropBreakpoint` property is used. The backdrop will be disabled up to and including the specified `backdropBreakpoint` and will be enabled after it.

When the backdrop is disabled, users will be able to interact with elements outside the sheet modal using a pointer or keyboard. Assistive technologies may not focus outside the sheet modal by default due to the usage of `aria-modal`. We recommend avoiding features such as autofocus here as it can cause assistive technologies to jump between two interactive contexts without warning the user.

## Performance

### Mounting Inner Contents

The content of an inline `ion-modal` is unmounted when closed. If this content is expensive to render, developers can use the `keepContentsMounted` property to mount the content as soon as the modal is mounted. This can help optimize the responsiveness of your application as the inner contents will have already been mounted when the modal opens.

**JavaScript**

```html
<ion-header>
  <ion-toolbar>
    <ion-title>Example</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Modal</ion-button>
  <ion-modal trigger="open-modal">
    <ion-header>
      <ion-toolbar>
        <ion-buttons slot="start">
          <ion-button id="close">Close</ion-button>
        </ion-buttons>
        <ion-title>Modal</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding"> This content was mounted as soon as the modal was created. </ion-content>
  </ion-modal>
</ion-content>

<script>
  var modal = document.querySelector('ion-modal');

  var closeBtn = document.querySelector('#close');
  closeBtn.addEventListener('click', () => close());

  var close = () => {
    modal.dismiss();
  };
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-header>
  <ion-toolbar>
    <ion-title>Example</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  <ion-button id="open-modal" expand="block">Open Modal</ion-button>
  <ion-modal [keepContentsMounted]="true" trigger="open-modal" #modal>
    <ng-template>
      <ion-header>
        <ion-toolbar>
          <ion-buttons slot="start">
            <ion-button (click)="modal.dismiss()">Cancel</ion-button>
          </ion-buttons>
          <ion-title>Modal</ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding"> This content was mounted as soon as the modal was created. </ion-content>
    </ng-template>
  </ion-modal>
</ion-content>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonButton, IonButtons, IonContent, IonHeader, IonModal, IonTitle, IonToolbar } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonButtons, IonContent, IonHeader, IonModal, IonTitle, IonToolbar],
})
export class ExampleComponent {}
```

**React**

```tsx
import React, { useRef } from 'react';
import { IonButtons, IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage } from '@ionic/react';

function Example() {
  const modal = useRef<HTMLIonModalElement>(null);

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Example</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent className="ion-padding">
        <IonButton id="open-modal" expand="block">
          Open Modal
        </IonButton>
        <IonModal ref={modal} keepContentsMounted={true} trigger="open-modal">
          <IonHeader>
            <IonToolbar>
              <IonButtons slot="start">
                <IonButton onClick={() => modal.current?.dismiss()}>Close</IonButton>
              </IonButtons>
              <IonTitle>Modal</IonTitle>
            </IonToolbar>
          </IonHeader>
          <IonContent className="ion-padding">This content was mounted as soon as the modal was created.</IonContent>
        </IonModal>
      </IonContent>
    </IonPage>
  );
}

export default Example;
```

**Vue**

```html
<template>
  <ion-header>
    <ion-toolbar>
      <ion-title>Example</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content class="ion-padding">
    <ion-button id="open-modal" expand="block">Open Modal</ion-button>
    <ion-modal ref="modal" :keep-contents-mounted="true" trigger="open-modal">
      <ion-header>
        <ion-toolbar>
          <ion-buttons slot="start">
            <ion-button @click="close()">Cancel</ion-button>
          </ion-buttons>
          <ion-title>Modal</ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding"> This content was mounted as soon as the modal was created. </ion-content>
    </ion-modal>
  </ion-content>
</template>

<script setup lang="ts">
  import { IonButtons, IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle } from '@ionic/vue';
  import { ref } from 'vue';

  const modal = ref();

  const close = () => modal.value.$el.dismiss();
</script>
```

Developers should keep the following in mind when using `keepContentsMounted`:

- This feature should be used as a last resort in order to deal with existing performance problems. Try to identify and resolve performance bottlenecks before using this feature. Additionally, do not use this to anticipate performance problems.
- This feature is only needed when using a JavaScript Framework. Developers not using a framework can pass the contents to be rendered into the modal, and the contents will be rendered automatically.
- This feature only works with inline modals. Modals created with the `modalController` are not created ahead of time, so the inner contents are not created either.
- Any JavaScript Framework lifecycle hooks on the inner component will run as soon as the modal is mounted, not when the modal is presented.

## Properties

### animated

**Description**: If `true`, the modal will animate.

**Attribute**: `animated`

**Type**: `boolean`

**Default**: `true`

### backdropBreakpoint

**Description**: A decimal value between 0 and 1 that indicates the point after which the backdrop will begin to fade in when using a sheet modal. Prior to this point, the backdrop will be hidden and the content underneath the sheet can be interacted with. This value is exclusive meaning the backdrop will become active after the value specified.

**Attribute**: `backdrop-breakpoint`

**Type**: `number`

**Default**: `0`

### backdropDismiss

**Description**: If `true`, the modal will be dismissed when the backdrop is clicked.

**Attribute**: `backdrop-dismiss`

**Type**: `boolean`

**Default**: `true`

### breakpoints

**Description**: The breakpoints to use when creating a sheet modal. Each value in the array must be a decimal between 0 and 1 where 0 indicates the modal is fully closed and 1 indicates the modal is fully open. Values are relative to the height of the modal, not the height of the screen. One of the values in this array must be the value of the `initialBreakpoint` property. For example: [0, .25, .5, 1]

**Attribute**: `undefined`

**Type**: `number[] | undefined`

**Default**: `undefined`

### canDismiss

**Description**: Determines whether or not a modal can dismiss when calling the `dismiss` method. If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss. If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss. See [https://ionicframework.com/docs/troubleshooting/runtime#accessing-this](https://ionicframework.com/docs/troubleshooting/runtime#accessing-this) if you need to access `this` from within the callback.

**Attribute**: `can-dismiss`

**Type**: `((data?: any, role?: string | undefined) => Promise<boolean>) | boolean`

**Default**: `true`

### enterAnimation

**Description**: Animation to use when the modal is presented.

**Attribute**: `undefined`

**Type**: `((baseEl: any, opts?: any) => Animation) | undefined`

**Default**: `undefined`

### expandToScroll

**Description**: Controls whether scrolling or dragging within the sheet modal expands it to a larger breakpoint. This only takes effect when `breakpoints` and `initialBreakpoint` are set. If `true`, scrolling or dragging anywhere in the modal will first expand it to the next breakpoint. Once fully expanded, scrolling will affect the content. If `false`, scrolling will always affect the content. The modal will only expand when dragging the header or handle. The modal will close when dragging the header or handle. It can also be closed when dragging the content, but only if the content is scrolled to the top.

**Attribute**: `expand-to-scroll`

**Type**: `boolean`

**Default**: `true`

### focusTrap

**Description**: If `true`, focus will not be allowed to move outside of this overlay. If `false`, focus will be allowed to move outside of the overlay. In most scenarios this property should remain set to `true`. Setting this property to `false` can cause severe accessibility issues as users relying on assistive technologies may be able to move focus into a confusing state. We recommend only setting this to `false` when absolutely necessary. Developers may want to consider disabling focus trapping if this overlay presents a non-Ionic overlay from a 3rd party library. Developers would disable focus trapping on the Ionic overlay when presenting the 3rd party overlay and then re-enable focus trapping when dismissing the 3rd party overlay and moving focus back to the Ionic overlay.

**Attribute**: `focus-trap`

**Type**: `boolean`

**Default**: `true`

### handle

**Description**: The horizontal line that displays at the top of a sheet modal. It is `true` by default when setting the `breakpoints` and `initialBreakpoint` properties.

**Attribute**: `handle`

**Type**: `boolean | undefined`

**Default**: `undefined`

### handleBehavior

**Description**: The interaction behavior for the sheet modal when the handle is pressed. Handle behavior is unavailable when the `handle` property is set to `false` or when the `breakpoints` property is not set (using a fullscreen or card modal). Set to `"cycle"` to make the handle focusable and let the sheet modal cycle between available breakpoints when pressed. This keeps the sheet operable with assistive technology. Set to `"none"` to make the handle purely decorative when pressed and removed from the tab order. Defaults to `"cycle"`.

**Attribute**: `handle-behavior`

**Type**: `"cycle" | "none" | undefined`

**Default**: `'cycle'`

### htmlAttributes

**Description**: Additional attributes to pass to the modal.

**Attribute**: `undefined`

**Type**: `undefined | { [key: string]: any; }`

**Default**: `undefined`

### initialBreakpoint

**Description**: A decimal value between 0 and 1 that indicates the initial point the modal will open at when creating a sheet modal. This value must also be listed in the `breakpoints` array.

**Attribute**: `initial-breakpoint`

**Type**: `number | undefined`

**Default**: `undefined`

### isOpen

**Description**: If `true`, the modal will open. If `false`, the modal will close. Use this if you need finer grained control over presentation, otherwise just use the modalController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the modal dismisses. You will need to do that in your code.

**Attribute**: `is-open`

**Type**: `boolean`

**Default**: `false`

### keepContentsMounted

**Description**: If `true`, the component passed into `ion-modal` will automatically be mounted when the modal is created. The component will remain mounted even when the modal is dismissed. However, the component will be destroyed when the modal is destroyed. This property is not reactive and should only be used when initially creating a modal. Note: This feature only applies to inline modals in JavaScript frameworks such as Angular, React, and Vue.

**Attribute**: `keep-contents-mounted`

**Type**: `boolean`

**Default**: `false`

### keyboardClose

**Description**: If `true`, the keyboard will be automatically dismissed when the overlay is presented.

**Attribute**: `keyboard-close`

**Type**: `boolean`

**Default**: `true`

### leaveAnimation

**Description**: Animation to use when the modal is dismissed.

**Attribute**: `undefined`

**Type**: `((baseEl: any, opts?: any) => Animation) | undefined`

**Default**: `undefined`

### mode

**Description**: The mode determines which platform styles to use. This is a [virtual property](/docs/core-concepts/fundamentals.md#virtual-properties) that is set once during initialization and will not update if you change its value after the initial render.

**Attribute**: `mode`

**Type**: `"ios" | "md"`

**Default**: `undefined`

### presentingElement

**Description**: The element that presented the modal. This is used for card presentation effects and for stacking multiple modals on top of each other. Only applies in iOS mode.

**Attribute**: `undefined`

**Type**: `HTMLElement | undefined`

**Default**: `undefined`

### showBackdrop

**Description**: If `true`, a backdrop will be displayed behind the modal. This property controls whether or not the backdrop darkens the screen when the modal is presented. It does not control whether or not the backdrop is active or present in the DOM.

**Attribute**: `show-backdrop`

**Type**: `boolean`

**Default**: `true`

### trigger

**Description**: An ID corresponding to the trigger element that causes the modal to open when clicked.

**Attribute**: `trigger`

**Type**: `string | undefined`

**Default**: `undefined`

## Events

| Name | Description | Bubbles |
| --- | --- | --- |
| `didDismiss` | Emitted after the modal has dismissed. Shorthand for ionModalDidDismiss. | `true` |
| `didPresent` | Emitted after the modal has presented. Shorthand for ionModalDidPresent. | `true` |
| `ionBreakpointDidChange` | Emitted after the modal breakpoint has changed. | `true` |
| `ionDragEnd` | Event that is emitted when the sheet modal or card modal gesture ends. | `true` |
| `ionDragMove` | Event that is emitted when the sheet modal or card modal gesture moves. | `true` |
| `ionDragStart` | Event that is emitted when the sheet modal or card modal gesture starts. | `true` |
| `ionModalDidDismiss` | Emitted after the modal has dismissed. | `true` |
| `ionModalDidPresent` | Emitted after the modal has presented. | `true` |
| `ionModalWillDismiss` | Emitted before the modal has dismissed. | `true` |
| `ionModalWillPresent` | Emitted before the modal has presented. | `true` |
| `willDismiss` | Emitted before the modal has dismissed. Shorthand for ionModalWillDismiss. | `true` |
| `willPresent` | Emitted before the modal has presented. Shorthand for ionModalWillPresent. | `true` |

## Methods

### dismiss

**Description**: Dismiss the modal overlay after it has been presented. This is a no-op if the overlay has not been presented yet. If you want to remove an overlay from the DOM that was never presented, use the [remove](https://developer.mozilla.org/en-US/docs/Web/API/Element/remove) method.

**Signature**: `dismiss(data?: any, role?: string) => Promise<boolean>`

**Parameters**: **data**: Any data to emit in the dismiss events. **role**: The role of the element that is dismissing the modal. For example, `cancel` or `backdrop`.

### getCurrentBreakpoint

**Description**: Returns the current breakpoint of a sheet style modal

**Signature**: `getCurrentBreakpoint() => Promise<number | undefined>`

### onDidDismiss

**Description**: Returns a promise that resolves when the modal did dismiss.

**Signature**: `onDidDismiss<T = any>() => Promise<OverlayEventDetail<T>>`

### onWillDismiss

**Description**: Returns a promise that resolves when the modal will dismiss.

**Signature**: `onWillDismiss<T = any>() => Promise<OverlayEventDetail<T>>`

### present

**Description**: Present the modal overlay after it has been created.

**Signature**: `present() => Promise<void>`

### setCurrentBreakpoint

**Description**: Move a sheet style modal to a specific breakpoint.

**Signature**: `setCurrentBreakpoint(breakpoint: number) => Promise<void>`

**Parameters**: **breakpoint**: The breakpoint value to move the sheet modal to. Must be a value defined in your `breakpoints` array.

## CSS Shadow Parts

| Name | Description |
| --- | --- |
| `backdrop` | The `ion-backdrop` element. |
| `content` | The wrapper element for the default slot. |
| `handle` | The handle that is displayed at the top of the sheet modal when `handle="true"`. |

## CSS Custom Properties

**iOS**

| Name | Description |
| --- | --- |
| `--backdrop-opacity` | Opacity of the backdrop |
| `--background` | Background of the modal content |
| `--border-color` | Border color of the modal content |
| `--border-radius` | Border radius of the modal content |
| `--border-style` | Border style of the modal content |
| `--border-width` | Border width of the modal content |
| `--height` | Height of the modal |
| `--max-height` | Maximum height of the modal |
| `--max-width` | Maximum width of the modal |
| `--min-height` | Minimum height of the modal |
| `--min-width` | Minimum width of the modal |
| `--width` | Width of the modal |

**MD**

| Name | Description |
| --- | --- |
| `--backdrop-opacity` | Opacity of the backdrop |
| `--background` | Background of the modal content |
| `--border-color` | Border color of the modal content |
| `--border-radius` | Border radius of the modal content |
| `--border-style` | Border style of the modal content |
| `--border-width` | Border width of the modal content |
| `--height` | Height of the modal |
| `--max-height` | Maximum height of the modal |
| `--max-width` | Maximum width of the modal |
| `--min-height` | Minimum height of the modal |
| `--min-width` | Minimum width of the modal |
| `--width` | Width of the modal |

## Slots

| Name | Description |
| --- | --- |
|  | Content is placed inside of the `.modal-content` element. |
