Search docs/
scoped

ion-modal

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.

Dismissing

The modal can be dismissed after creation by calling the dismiss() method on the modal controller. The onDidDismiss function can be called to perform an action after the modal is dismissed.

Customization

Modal uses scoped encapsulation, which means it will automatically scope its CSS by appending each of the styles with an additional class at runtime. Overriding scoped selectors in CSS requires a higher specificity selector.

We recommend passing a custom class to cssClass in the create method and using that to add custom styles to the host and inner elements. This property can also accept multiple classes separated by spaces. View the Usage section for an example of how to pass a class using cssClass.

/* DOES NOT WORK - not specific enough */
.modal-wrapper {
  background: #222;
}

/* Works - pass "my-custom-class" in cssClass to increase specificity */
.my-custom-class .modal-wrapper {
  background: #222;
}
CopyCopied

Any of the defined CSS Custom Properties can be used to style the Modal without needing to target individual elements:

.my-custom-class {
  --background: #222;
}
CopyCopied

If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file. Read Style Placement in the Angular section below for more information.

Usage

import { Component } from '@angular/core';
import { ModalController } from '@ionic/angular';
import { ModalPage } from '../modal/modal.page';

@Component({
  selector: 'modal-example',
  templateUrl: 'modal-example.html',
  styleUrls: ['./modal-example.css']
})
export class ModalExample {
  constructor(public modalController: ModalController) {

  }

  async presentModal() {
    const modal = await this.modalController.create({
      component: ModalPage,
      cssClass: 'my-custom-class'
    });
    return await modal.present();
  }
}
CopyCopied
import { Component, Input } from '@angular/core';

@Component({
  selector: 'modal-page',
})
export class ModalPage {

  constructor() {}

}
CopyCopied

Passing Data

During creation of a modal, data can be passed in through the componentProps. The previous example can be written to include data:

async presentModal() {
  const modal = await this.modalController.create({
    component: ModalPage,
    cssClass: 'my-custom-class',
    componentProps: {
      'firstName': 'Douglas',
      'lastName': 'Adams',
      'middleInitial': 'N'
    }
  });
  return await modal.present();
}
CopyCopied

To get the data passed into the componentProps, set it as an @Input:

export class ModalPage {

  // Data passed in by componentProps
  @Input() firstName: string;
  @Input() lastName: string;
  @Input() middleInitial: string;

}
CopyCopied

Dismissing a Modal

A modal can be dismissed by calling the dismiss method on the modal controller and optionally passing any data from the modal.

export class ModalPage {
  ...

  dismiss() {
    // using the injected ModalController this page
    // can "dismiss" itself and optionally pass back data
    this.modalCtrl.dismiss({
      'dismissed': true
    });
  }
}
CopyCopied

After being dismissed, the data can be read in through the onWillDismiss or onDidDismiss attached to the modal after creation:

const { data } = await modal.onWillDismiss();
console.log(data);
CopyCopied

Lazy Loading

When lazy loading a modal, it's important to note that the modal will not be loaded when it is opened, but rather when the module that imports the modal's module is loaded.

For example, say there exists a CalendarComponent and an EventModal. The modal is presented by clicking a button in the CalendarComponent. In Angular, the EventModalModule would need to be included in the CalendarComponentModule since the modal is created in the CalendarComponent:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';

import { CalendarComponent } from './calendar.component';
import { EventModalModule } from '../modals/event/event.module';

@NgModule({
  declarations: [
    CalendarComponent
  ],
  imports: [
    IonicModule,
    CommonModule,
    EventModalModule
  ],
  exports: [
    CalendarComponent
  ]
})

export class CalendarComponentModule {}
CopyCopied

Swipeable Modals

Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.

If you are creating an application that uses ion-tabs, it is recommended that you get the parent ion-router-outlet using this.routerOutlet.parentOutlet.nativeEl, otherwise the tabbar will not scale down when the modal opens.

import { IonRouterOutlet } from '@ionic/angular';

constructor(private routerOutlet: IonRouterOutlet) {}

async presentModal() {
  const modal = await this.modalController.create({
    component: ModalPage,
    cssClass: 'my-custom-class',
    swipeToClose: true,
    presentingElement: this.routerOutlet.nativeEl
  });
  return await modal.present();
}
CopyCopied

In most scenarios, using the ion-router-outlet element as the presentingElement is fine. In cases where you are presenting a card-style modal from within another modal, you should pass in the top-most ion-modal element as the presentingElement.

import { ModalController } from '@ionic/angular';

constructor(private modalCtrl: ModalController) {}

async presentModal() {
  const modal = await this.modalController.create({
    component: ModalPage,
    cssClass: 'my-custom-class',
    swipeToClose: true,
    presentingElement: await this.modalCtrl.getTop() // Get the top-most ion-modal
  });
  return await modal.present();
}
CopyCopied

Style Placement

In Angular, the CSS of a specific page is scoped only to elements of that page. Even though the Modal can be presented from within a page, the ion-modal element is appended outside of the current page. This means that any custom styles need to go in a global stylesheet file. In an Ionic Angular starter this can be the src/global.scss file or you can register a new global style file by adding to the styles build option in angular.json.

customElements.define('modal-page', class extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `
<ion-header>
  <ion-toolbar>
    <ion-title>Modal Header</ion-title>
    <ion-buttons slot="primary">
      <ion-button onClick="dismissModal()">
        <ion-icon slot="icon-only" name="close"></ion-icon>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
  Modal Content
</ion-content>`;
  }
});

function presentModal() {
  // create the modal with the `modal-page` component
  const modalElement = document.createElement('ion-modal');
  modalElement.component = 'modal-page';
  modalElement.cssClass = 'my-custom-class';

  // present the modal
  document.body.appendChild(modalElement);
  return modalElement.present();
}
CopyCopied

Passing Data

During creation of a modal, data can be passed in through the componentProps. The previous example can be written to include data:

const modalElement = document.createElement('ion-modal');
modalElement.component = 'modal-page';
modalElement.cssClass = 'my-custom-class';
modalElement.componentProps = {
  'firstName': 'Douglas',
  'lastName': 'Adams',
  'middleInitial': 'N'
};
CopyCopied

To get the data passed into the componentProps, query for the modal in the modal-page:

customElements.define('modal-page', class extends HTMLElement {
  connectedCallback() {
    const modalElement = document.querySelector('ion-modal');
    console.log(modalElement.componentProps.firstName);

    ...
  }
}
CopyCopied

Dismissing a Modal

A modal can be dismissed by calling the dismiss method and optionally passing any data from the modal.

async function dismissModal() {
  await modal.dismiss({
    'dismissed': true
  });
}
CopyCopied

After being dismissed, the data can be read in through the onWillDismiss or onDidDismiss attached to the modal after creation:

const { data } = await modalElement.onWillDismiss();
console.log(data);
CopyCopied

Swipeable Modals

Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.

const modalElement = document.createElement('ion-modal');
modalElement.component = 'modal-page';
modalElement.cssClass = 'my-custom-class';
modalElement.swipeToClose = true;
modalElement.presentingElement = document.querySelector('ion-nav');
CopyCopied

In most scenarios, using the ion-nav element as the presentingElement is fine. In cases where you are presenting a card-style modal from within a modal, you should pass in the top-most ion-modal element as the presentingElement.

const modalElement = document.createElement('ion-modal');
modalElement.component = 'modal-page';
modalElement.cssClass = 'my-custom-class';
modalElement.swipeToClose = true;
modalElement.presentingElement = await modalController.getTop(); // Get the top-most ion-modal
CopyCopied
import React, { useState } from 'react';
import { IonModal, IonButton, IonContent } from '@ionic/react';

export const ModalExample: React.FC = () => {
  const [showModal, setShowModal] = useState(false);

  return (
    <IonContent>
      <IonModal isOpen={showModal} cssClass='my-custom-class'>
        <p>This is modal content</p>
        <IonButton onClick={() => setShowModal(false)}>Close Modal</IonButton>
      </IonModal>
      <IonButton onClick={() => setShowModal(true)}>Show Modal</IonButton>
    </IonContent>
  );
};
CopyCopied

Swipeable Modals

Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.

<IonModal
  isOpen={showModal}
  cssClass='my-custom-class'
  swipeToClose={true}
  presentingElement={pageRef.current}
  onDidDismiss={() => setShowModal(false)}>
    <p>This is modal content</p>
    <IonButton onClick={() => setShowModal(false)}>Close Modal</IonButton>
</IonModal>
CopyCopied

In most scenarios, setting a ref on IonPage and passing that ref's current value to presentingElement is fine. In cases where you are presenting a card-style modal from within another modal, you should pass in the top-most ion-modal ref as the presentingElement.

<IonModal
  ref={firstModalRef}
  isOpen={showModal}
  cssClass='my-custom-class'
  swipeToClose={true}
  presentingElement={pageRef.current}
  onDidDismiss={() => setShowModal(false)}>
    <p>This is modal content</p>
    <IonButton onClick={() => setShow2ndModal(true)}>Show 2nd Modal</IonButton>
    <IonButton onClick={() => setShowModal(false)}>Close Modal</IonButton>
</IonModal>
<IonModal
  isOpen={show2ndModal}
  cssClass='my-custom-class'
  presentingElement={firstModalRef.current}
  onDidDismiss={() => setShow2ndModal(false)}>
  <p>This is more modal content</p>
  <IonButton onClick={() => setShow2ndModal(false)}>Close Modal</IonButton>
</IonModal>
CopyCopied
import { Component, h } from '@stencil/core';

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

@Component({
  tag: 'modal-example',
  styleUrl: 'modal-example.css'
})
export class ModalExample {
  async presentModal() {
    const modal = await modalController.create({
      component: 'page-modal',
      cssClass: 'my-custom-class'
    });
    await modal.present();
  }
}
CopyCopied
import { Component, h } from '@stencil/core';

@Component({
  tag: 'page-modal',
  styleUrl: 'page-modal.css',
})
export class PageModal {
  render() {
    return [
      <ion-list>
        <ion-item>
          <ion-label>Documentation</ion-label>
        </ion-item>
        <ion-item>
          <ion-label>Feedback</ion-label>
        </ion-item>
        <ion-item>
          <ion-label>Settings</ion-label>
        </ion-item>
      </ion-list>
    ];
  }
}
CopyCopied

Passing Data

During creation of a modal, data can be passed in through the componentProps. The previous example can be written to include data:

async presentModal() {
  const modal = await modalController.create({
    component: 'page-modal',
    cssClass: 'my-custom-class',
    componentProps: {
      'firstName': 'Douglas',
      'lastName': 'Adams',
      'middleInitial': 'N'
    }
  });
  await modal.present();
}
CopyCopied

To get the data passed into the componentProps, set each one as a @Prop:

import { Component, Prop, h } from '@stencil/core';

@Component({
  tag: 'page-modal',
  styleUrl: 'page-modal.css',
})
export class PageModal {
  // Data passed in by componentProps
  @Prop() firstName: string;
  @Prop() lastName: string;
  @Prop() middleInitial: string;
}
CopyCopied

Dismissing a Modal

A modal can be dismissed by calling the dismiss method on the modal controller and optionally passing any data from the modal.

export class ModalPage {
  ...

  dismiss(data?: any) {
    // dismiss the closest modal and optionally pass back data
    (this.el.closest('ion-modal') as any).dismiss({
      'dismissed': true
    });
  }
}
CopyCopied

After being dismissed, the data can be read in through the onWillDismiss or onDidDismiss attached to the modal after creation:

const { data } = await modal.onWillDismiss();
console.log(data);
CopyCopied

Swipeable Modals

Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.

import { Component, Element, h } from '@stencil/core';

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

@Component({
  tag: 'modal-example',
  styleUrl: 'modal-example.css'
})
export class ModalExample {
  @Element() el: any;

  async presentModal() {
    const modal = await modalController.create({
      component: 'page-modal',
      cssClass: 'my-custom-class',
      swipeToClose: true,
      presentingElement: this.el.closest('ion-router-outlet'),
    });
    await modal.present();
  }

}
CopyCopied

In most scenarios, using the ion-router-outlet element as the presentingElement is fine. In cases where you are presenting a card-style modal from within another modal, you should pass in the top-most ion-modal element as the presentingElement.

async presentModal() {
  const modal = await modalController.create({
    component: 'page-modal',
    cssClass: 'my-custom-class',
    swipeToClose: true,
    presentingElement: await modalController.getTop() // Get the top-most ion-modal
  });
  await modal.present();
}
CopyCopied
<template>
  <div>
    <ion-header>
      <ion-toolbar>
        <ion-title>{{ title }}</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">
      {{ content }}
    </ion-content>
  </div>
</template>

<script>
export default {
  name: 'Modal',
  props: {
    title: { type: String, default: 'Super Modal' },
  },
  data() {
    return {
      content: 'Content',
    }
  },
}
</script>
CopyCopied
<template>
  <ion-page class="ion-page">
    <ion-content class="ion-content ion-padding">
      <ion-button @click="openModal">Open Modal</ion-button>
    </ion-content>
  </ion-page>
</template>

<script>
import Modal from './modal.vue'

export default {
  methods: {
    openModal() {
      return this.$ionic.modalController
        .create({
          component: Modal,
          cssClass: 'my-custom-class',
          componentProps: {
            data: {
              content: 'New Content',
            },
            propsData: {
              title: 'New title',
            },
          },
        })
        .then(m => m.present())
    },
  },
}
</script>
CopyCopied

Properties

animated

Description

If true, the modal will animate.

Attributeanimated
Typeboolean
Defaulttrue

backdropDismiss

Description

If true, the modal will be dismissed when the backdrop is clicked.

Attributebackdrop-dismiss
Typeboolean
Defaulttrue

component

Description

The component to display inside of the modal.

Attributecomponent
TypeFunction | HTMLElement | null | string

componentProps

Description

The data to pass to the modal component.

Typeundefined | { [key: string]: any; }

cssClass

Description

Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces.

Attributecss-class
Typestring | string[] | undefined

enterAnimation

Description

Animation to use when the modal is presented.

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

keyboardClose

Description

If true, the keyboard will be automatically dismissed when the overlay is presented.

Attributekeyboard-close
Typeboolean
Defaulttrue

leaveAnimation

Description

Animation to use when the modal is dismissed.

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

mode

Description

The mode determines which platform styles to use.

Attributemode
Type"ios" | "md"

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.

TypeHTMLElement | undefined

showBackdrop

Description

If true, a backdrop will be displayed behind the modal.

Attributeshow-backdrop
Typeboolean
Defaulttrue

swipeToClose

Description

If true, the modal can be swiped to dismiss. Only applies in iOS mode.

Attributeswipe-to-close
Typeboolean
Defaultfalse

Events

NameDescription
ionModalDidDismissEmitted after the modal has dismissed.
ionModalDidPresentEmitted after the modal has presented.
ionModalWillDismissEmitted before the modal has dismissed.
ionModalWillPresentEmitted before the modal has presented.

Methods

dismiss

Description

Dismiss the modal overlay after it has been presented.

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

onDidDismiss

Description

Returns a promise that resolves when the modal did dismiss.

SignatureonDidDismiss<T = any>() => Promise<OverlayEventDetail<T>>

onWillDismiss

Description

Returns a promise that resolves when the modal will dismiss.

SignatureonWillDismiss<T = any>() => Promise<OverlayEventDetail<T>>

present

Description

Present the modal overlay after it has been created.

Signaturepresent() => Promise<void>

CSS Custom Properties

NameDescription
--backdrop-opacityOpacity of the backdrop
--backgroundBackground of the modal content
--border-colorBorder color of the modal content
--border-radiusBorder radius of the modal content
--border-styleBorder style of the modal content
--border-widthBorder width of the modal content
--heightHeight of the modal
--max-heightMaximum height of the modal
--max-widthMaximum width of the modal
--min-heightMinimum height of the modal
--min-widthMinimum width of the modal
--widthWidth of the modal
View Source