December 14, 2021
  • Tutorials
  • Angular
  • Ionic

5 Examples of the new Ionic 6 Bottom Sheet Modal

Simon Grimm

This is a guest post from Simon Grimm, Ionic Insider and educator at the Ionic Academy. Simon just released a brand new eBook called Built with Ionic where he breaks down popular apps like Netflix and rebuilds them completely with Ionic styling and interactions!

The Ionic 6 modal comes with a brand new presentation mode called bottom sheet, which is usually also known as bottom drawer in mobile applications.

It is the third presentation style besides the default full screen modal and the iOS card modal, and in this tutorial we will explore five ways to use the new presentation.

Ionic App Prerequisite

All you need to check out the following examples is a blank Ionic app, so go ahead and create a new app if you need one for testing:

ionic start bottomModal blank --type=angular

If you use your own project, make sure your app is using Ionic 6 by running the following command and then checking your package.json afterwards:

npm i @ionic/angular@latest

Note: Make sure your project is under source control before you migrate to a major new version and check out the breaking changes of v6 or follow the v6 upgrade guide for information about React and Vue as well.

1. Ionic Bottom Sheet Modal Basic

Let’s start with the basics – how can I get a bottom sheet modal?

For this simple case we generate a new page that will act as our content:

ionic g page simpleModal

You could leave that page empty or put in a bit of dummy content, it won’t change the presentation logic.

Anyway, if you want to see some elements on the screen, quickly bring up the src/app/simple-modal/simple-modal.page.html and change it to:

<ion-header>
  <ion-toolbar>
    <ion-title>Modal Content</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>

  <ion-accordion-group>
    <ion-accordion>
      <ion-item slot="header">
        <ion-label>People</ion-label>
      </ion-item>

      <ion-list slot="content">
        <ion-item>
          <ion-label>Max</ion-label>
        </ion-item>
        <ion-item>
          <ion-label>Mike</ion-label>
        </ion-item>
        <ion-item>
          <ion-label>Kim</ion-label>
        </ion-item>
      </ion-list>
    </ion-accordion>

  </ion-accordion-group>

</ion-content>

Oh, you never saw the ion-accordion-group before? Another new component of Ionic 6, and we will talk about that in another dedicated post!

For now we need to focus on the presentation of the modal, and to get the bottom sheet you have to set the breakpoints and initialBreakpoints value when creating the modal.

Let’s do this right now by changing the src/app/home/home.page.ts to:

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

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  constructor(private modalCtrl: ModalController) {}

  async presentModal() {
    const modal = await this.modalCtrl.create({
      component: SimpleModalPage,
      breakpoints: [0, 0.3, 0.5, 0.8],
      initialBreakpoint: 0.5
    });
    await modal.present();
  }
}

The breakpoints value is an array of values between 0 and 1, and defines at which percentage of your screen the modal snaps when the user drags it.

When the modal comes up, it will open initially to the initialBreakpoint, which needs to be one value that you’ve added to the array of breakpoints.

It’s really that easy, and you might already have tons of ideas to use this new modal. Let’s take a look at another helpful example.

2. Pass Data to your Modal with Background Update

Because the bottom sheet usually doesn’t cover your whole screen, reacting to changes made in the modal becomes more important.

But there’s an easy solution to keep an active link between your page and the modal, and we will do this within another new page:

ionic g page updateModal

The idea is quite easy: We pass a BehaviorSubject into the componentProps of the modal just like we would pass any other input.

By doing this, we can subscribe() to the changes on our page and later emit new values within the modal!

I’ve used this pattern inside my new eBook Built with Ionic – check it out if you want to build even more epic interfaces with Animations and Gestures!

Imagine you want to display a selection of the payment method, which could be easily handled with the bottom sheet.

We can start by presenting the modal with the Subject inside the src/app/home/home.page.ts:

import { Component } from '@angular/core';
import { ModalController } from '@ionic/angular';
import { BehaviorSubject } from 'rxjs';
import { UpdateModalPage } from '../update-modal/update-modal.page';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  choice = 'Credit Card';

  constructor(private modalCtrl: ModalController) {}

  async presentModal() {
    const mySubject = new BehaviorSubject(this.choice);

    const modal = await this.modalCtrl.create({
      component: UpdateModalPage,
      breakpoints: [0, 0.2],
      initialBreakpoint: 0.2,
      handle: false,
      componentProps: {
        mySubject
      },
    });
    await modal.present();

    mySubject.subscribe((value: string) => {
      this.choice = value;
    });

    modal.onDidDismiss().then((_ => {
      mySubject.unsubscribe();
    }));
  }
}

Of course we also unsubscribe() when the modal is dismissed to prevent memory leaks!

Now we can change the src/app/home/home.page.html so we can see our choice in the background of the main page:

<ion-header>
  <ion-toolbar color="primary">
    <ion-title>
      Ionic Modal
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
  <ion-text color="tertiary" class="ion-text-center">
    <h2>Your choice: {{ choice }}</h2>
  </ion-text>
  <ion-button expand="full" (click)="presentModal()">Show modal</ion-button>
</ion-content>

Next step is defining the @Input() in our modal page, and a bit of additional logic so we can switch between different values on that page.

To build a simple UI we not only need the string value but also a number to handle the change more easily (and use some conditional styling), so go ahead and change the src/app/update-modal/update-modal.page.ts to this:

import { Component, Input, OnInit } from '@angular/core';
import { ModalController } from '@ionic/angular';
import { BehaviorSubject } from 'rxjs';

@Component({
  selector: 'app-update-modal',
  templateUrl: './update-modal.page.html',
  styleUrls: ['./update-modal.page.scss'],
})
export class UpdateModalPage implements OnInit {
  choice = 0;
  @Input() mySubject: BehaviorSubject<string>;
  choices = [
    'Credit Card',
    'Paypal',
    'Bank transfer'
  ];

  constructor(private modalCtrl: ModalController) { }

  // Set the initial choice based on the input
  ngOnInit() {
    const preselect = this.mySubject.value;    
    this.choice = this.choices.indexOf(preselect);
  }

  // Change the choice and update the Subject
  changeChoice(num) {
    this.choice = num;
    this.mySubject.next(this.choices[num]);
  }

  close() {
    this.modalCtrl.dismiss();
  }
}

We now have the current selection as a number inside choice, so we can build simple logic with three buttons and conditional styling around it within our src/app/update-modal/update-modal.page.html:

<ion-content>
  <ion-row>
    <ion-col size="12" class="ion-text-center" class="category-select">
      <ion-button size="small" shape="round" (click)="changeChoice(0)" [class.selected]="choice == 0">
        Credit Card
      </ion-button>
      <ion-button size="small" shape="round" (click)="changeChoice(1)" [class.selected]="choice == 1">
        Paypal
      </ion-button>
      <ion-button size="small" shape="round" (click)="changeChoice(2)" [class.selected]="choice == 2">
        Bank transfer
      </ion-button>
    </ion-col>
  </ion-row>

  <ion-button expand="block" color="primary" strong="true" (click)="close()" class="ion-margin-top">
    Confirm
  </ion-button>
</ion-content>

Every button will now select a different value, and the changeChoice() function will update the subject – the parent immediately gets the new choice!

To make the selection look more pretty you can add some easy CSS inside the src/app/update-modal/update-modal.page.scss like this:

.category-select {
  ion-button {
    color: var(--ion-color-primary);
    --background: transparent;
    --background-activated: var(--ion-color-primary);
  }

  .selected {
    font-weight: 600;
    color: #fff;
    --background: var(--ion-color-primary);
  }
}

Selecting a new payment method was never more fun!

Of course this was just the most basic example, but presenting small overlays like this for a quick selection can really unclutter the rest of your form or overall UI!

Speaking of simplifying things…

3. Using an Inline Modal

Ever think you just need a simple modal and don’t want the usual way by creating another page, importing it, putting in your styling…?

Turns out that’s easier than ever by directly using a modal inline and adding a trigger!

The only thing we actually need is a piece of HTML that defines an id like the ion-button below.

Now we can set the trigger on the modal to the same value and boom – clicking the trigger brings up the modal.

Go ahead and change the src/app/home/home.page.html to this:

<ion-header>
  <ion-toolbar color="primary">
    <ion-title>
      Ionic Modal
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">

  <ion-button expand="full" id="openModal">
    I open the modal
  </ion-button>

  <ion-modal trigger="openModal" [initialBreakpoint]="0.5" [breakpoints]="[0, 0.5, 1]">
    <ng-template>
      <ion-header>
        <ion-toolbar color="primary">
          <ion-title>
            Inline
          </ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content>
        <ion-item>
          This is so cool!
          <p>And a bit funny</p>
        </ion-item>
      </ion-content>
    </ng-template>
  </ion-modal>

</ion-content>

See how we can even inject our breakpoints and initialBreakpoint?

This is a super convenient way if you need a simple modal and not the overhead of adding another page to your project.

4. Use the Modal as a Toast

Having scrollable content inside the modal is pretty cool, but have you thought about using it for a toast-like presentation of some information?

You can certainly do it with just a bit of additional styling!

Again, let’s generate a new page first:

ionic g page toastModal

The creation of the modal is mostly the same, but this time we also set the handle property to false so we get rid of that little grey notch at the top, and we also define a custom cssClass.

On top of that we use setTimeout() to directly hide the todal (leave a comment with a better name for this!) again, so let’s change the src/app/home/home.page.ts to this now:

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

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  constructor(private modalCtrl: ModalController) { }

  async presentModal() {
    const modal = await this.modalCtrl.create({
      component: ToastModalPage,
      breakpoints: [0, 0.3],
      initialBreakpoint: 0.3,
      cssClass: 'custom-modal',
      handle: false
    });
    await modal.present();

    // Optional: Hide the modal after a duration!
    setTimeout(() => {
      modal.dismiss();
    }, 2000);
  }

}

For the content itself we need to be careful now, as we don’t really want a scrollable area and also need a transparent background to make it look better.

Therefore we just wrap the content in our src/app/toast-modal/toast-modal.page.html within a simple div and replace everything inside the file with:

<div class="ion-padding ion-text-center content">

  <ion-text color="primary">
    <h2>Great success!</h2>
  </ion-text>
  <ion-icon name="checkmark-done-circle-outline" size="large" color="success"></ion-icon>

</div>

To make our element stand out with some background, we can add the following as a simple box styling to the src/app/toast-modal/toast-modal.page.scss:

.content {
    background: #fff;
    margin: 20px;
    border-radius: 8px;
}

You’ll notice that at this point your modal is still taking up the full width. In fact it’s actually just the background color of the modal.

Good thing we added a custom CSS class when we created the modal, so now we can style the modal at the top of our application inside the src/global.scss:

.custom-modal {
    --background: transparent;
}

Now the modal has no background anymore, and since our simple box has margin it looks like it’s floating in the air.

Of course, a super simple toast like this could be created with the Ionic Toast as well, but when you think about adding some buttons or a bit more functionality, this alternative to a standard toast will suit you a lot better!

5. Custom Styling with Parts

We’ve scratched the surface of styling in the previous example, but let’s see exactly how to customize every piece of the bottom sheet modal.

Bottom Sheet customization

Again, it’s important to add your custom CSS class to the creation for this, so one last time change the src/app/home/home.page.ts to reuse our initial page:

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

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  constructor(private modalCtrl: ModalController) { }

  async presentModal() {
    const modal = await this.modalCtrl.create({
      component: SimpleModalPage,
      breakpoints: [0, 0.4, 1],
      initialBreakpoint: 0.4,
      cssClass: 'custom-modal'
    });
    await modal.present();
  }

}

Now you can theme your modal with different CSS properties, but to get full control you have to use the documented shadow parts!

If you have never used parts make sure to check out the introduction to CSS Shadow Parts from Brandy as well!

The TL;DR version is this: You usually can’t style a shadow component from the outside and only inject styling through the CSS variables that Ionic offers for every component.

However, if a part is declared within a component, you can directly target that element even if it’s within the Shadow DOM and inject your own styling!

This means for our modal we can use these three available parts:

  • backdrop: The darkish backdrop that covers the rest of your app
  • handle: The top notch that is added at the top of the modal when using the bottom sheet presentation
  • content: The actual content of the modal, the page you display

So let’s have some fun with all of these properties and change the src/global.scss to:

.custom-modal {
    --backdrop-opacity: 1;
    backdrop-filter: blur(3px);

    &::part(backdrop) {
        background: #000;
    }

    &::part(handle) {
        background: var(--ion-color-primary);
        width: 150px;
        height: 6px;
    }

    &::part(content) {
        border-radius: 0px;
        box-shadow: 0px 0px 20px 5px rgb(0 0 0 / 32%);
    }
}

We now got a blurry transparent background, a bigger notch and a modal with drop shadow and no borders!

Perhaps not the final styling you want to use in your own app, but it definitely shows that you can customize every property of the Ionic modal!

Future: Handle Drag Events

Initially I also wanted to build out something even more powerful in combination with Ionic animations, but as of now it’s not possible to listen to changes of the modal breakpoints / position.

This is an open issue that we might see implemented in the near future, which would allow us to build even more powerful animations in combination with the modal.

For example, take a look at the Google Maps application:

When you select a location and pull up the bottom sheet, at some point the header search bar disappears. I’m very confident that we can (and will..) build this with Ionic soon!

It’s like the icing on the cake, and I’m pretty sure we’ll have a lot of fun with this component in the future.

Make it Yours

These were just five examples of how you could integrate the new Ionic 6 bottom sheet presentation of a modal – what will you use it for?

Leave a comment with more ideas on how you’ll use this component to improve your Ionic apps, and of course make sure you update your apps to Ionic 6 as soon as possible to benefit from all the new components!

For everyone who wants learn Ionic faster, I’m also running a full online school with 70+ Ionic video courses, templates and a supportive community — go check out the Ionic Academy and get access to a ton of learning material and build epic Ionic apps.

I have also released a new book, “Built WIth Ionic”, all based on Ionic V6. For the launch week there’s a 20% discount on all packages. The offer ends December 17th!

PS: And don’t forget to subscribe to my YouTube channel for fresh Ionic tutorials every week!


Simon Grimm