Skip to content
View lessons Login
3d cartoon hands holding a phone

Unlock full course by purchasing a membership

Adding Ionic to an Angular Application

Adding Ionic to an Angular Application

We’ve spent a little while working with a standard Angular application generated using the Angular CLI. We’ve also already created an Ionic application using the Ionic CLI. This is great because it sets up everything for us out of the box, and we can just start building!

But… it does muddy the waters a bit. If our application already includes Ionic by default then it is kind of hard to tell what is “Ionic” and what is “Angular”. We’ve let the Ionic CLI handle the jump from an “Angular” application to an “Ionic/Angular” application for us.

This is most certainly what you should do when building Ionic applications: use the Ionic CLI to generate the application for you. However, it is useful as a learning exercise to start off with a standard Angular application generated with the Angular CLI and add Ionic to it manually. Not only will this show us what Ionic changes about a standard Angular application, it is also going to give us some experience setting up and configuring a third party library in Angular.

It is also useful to demonstrate that all of the Angular knowledge you are gaining by learning Ionic is also applicable more broadly to Angular applications in general.

So, that is exactly what we are going to do in this lesson!

Generate an application with the Angular CLI

First, let’s generate a new Angular application with the Angular CLI:

ng new angular-to-ionic

You can use the following configurations:

✔ Do you want to create a 'zoneless' application without zone.js (Developer Preview)? No
✔ Which stylesheet format would you like to use? Sass (SCSS) [ https://sass-lang.com/documentation/syntax#scss ]
✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? No

Install Ionic Dependencies

Now we will need to install Ionic itself, which we can do by installing the following two packages:

npm install @ionic/angular ionicons

Add Ionic config file

Although we will not be making particular use of it right now, Ionic projects come with a configuration file to configure various things. Let’s set that up now.

{
"name": "angular-to-ionic",
"integrations": {},
"type": "angular-standalone"
}

Provide Ionic to the Application

One of the most important configutations is making sure that we update our app/app.config.ts file to:

  • Add provideIonicAngular to the providers
  • Use the IonicRouteStrategy
  • Use PreloadAllModules
import {
ApplicationConfig,
provideBrowserGlobalErrorListeners,
provideZonelessChangeDetection,
} from '@angular/core';
import {
RouteReuseStrategy,
provideRouter,
withPreloading,
PreloadAllModules,
} from '@angular/router';
import {
IonicRouteStrategy,
provideIonicAngular,
} from '@ionic/angular/standalone';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
provideIonicAngular(),
provideBrowserGlobalErrorListeners(),
provideZonelessChangeDetection(),
provideRouter(routes, withPreloading(PreloadAllModules)),
],
};

In the code above, we are saying we want to provide the RouteReuseStrategy token, but we want to use the class IonicRouteStrategy for that token. That means that wherever the RouteReuseStrategy token is used, it will actually use an instance of IonicRouteStrategy.

Route reuse strategies are a much more advanced topic, and you don’t really ever need to consider it too much. The key idea is that, by default, whenever we navigate between components in Angular, Angular will destroy the component that was just navigated away from. If we later come back to that route the component will be recreated again. This might not always be what you want, and Ionic defines some more specific rules around when components should be created and destroyed.

This is where Ionic’s lifecycle hooks like ionViewWillEnter and ionViewWillLeave come into play. These are like Angular’s ngOnInit lifecycle hook, except that they run at times related to the Ionic Page Lifecycle. We will not often use Ionic’s specific hooks, but consider a situation where you are running some logic using Angular’s ngOnInit hook. This only runs when the component is first created. If, according to Ionic’s route reuse strategy, some component is not destroyed when we navigate away from it, then the ngOnInit logic will not run when we navigate back to it. It is rarer, but in situations like this we might need to explicitly run some logic inside of a hook like ionViewWillEnter which will run every time we navigate to that page, regardless of whether it has already been created or not.

Update index.html

You will also find that an Ionic project has a few mobile specific changes made to the index.html file. Compare the index.html file of a standard Angular project:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AngularToIonic</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

to what we need to update our index.html file to for Ionic.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>AngularToIonic</title>
<base href="/" />
<meta name="color-scheme" content="light dark" />
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<link rel="icon" type="image/x-icon" href="favicon.ico">
<!-- add to homescreen for ios -->
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
</head>
<body>
<app-root></app-root>
</body>
</html>

Add Ionic CSS imports

Ionic also comes with some additional styling configuration.

/* Core CSS required for Ionic components to work properly */
@import "@ionic/angular/css/core.css";
/* Basic CSS for apps built with Ionic */
@import "@ionic/angular/css/normalize.css";
@import "@ionic/angular/css/structure.css";
@import "@ionic/angular/css/typography.css";
@import "@ionic/angular/css/display.css";
/* Optional CSS utils that can be commented out */
@import "@ionic/angular/css/padding.css";
@import "@ionic/angular/css/float-elements.css";
@import "@ionic/angular/css/text-alignment.css";
@import "@ionic/angular/css/text-transformation.css";
@import "@ionic/angular/css/flex-utils.css";
/**
* Ionic Dark Mode
* -----------------------------------------------------
* For more info, please see:
* https://ionicframework.com/docs/theming/dark-mode
*/
/* @import "@ionic/angular/css/palettes/dark.always.css"; */
/* @import "@ionic/angular/css/palettes/dark.class.css"; */
@import '@ionic/angular/css/palettes/dark.system.css';

Change the Router

<ion-app>
<ion-router-outlet />
</ion-app>
import { Component } from '@angular/core';
import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone';
@Component({
selector: 'app-root',
imports: [IonApp, IonRouterOutlet],
templateUrl: './app.html',
styleUrl: './app.scss',
})
export class App {
title = 'angular-to-ionic';
}

This is an important one, and there are two things going on here. One is that we are using the <ion-router-outlet> instead of the default Angular <router-outlet>. By default, Angular will apply mobile style transition animations when switching between views, and using Ionic’s version of the router outlet is what will enable this. We also surround our entire application in the <ion-app> component which is important for styling.

Try out some components

That should be all we need to do for a basic set up. Usually we would serve an Ionic application with the Ionic CLI using ionic serve but since we are just using a normal Angular application here we can instead just do ng serve.

If you do that you should see you have a working, but blank, application. That’s no fun though, so let’s set up a few of Ionic’s components so that we can actually see something.

To get the full sense of why using Ionic’s own router is useful, let’s set up two pages that the router outlet will route to: a home page and a detail page so that we can actually see some navigation occurring between two pages.

import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
import {
IonContent,
IonHeader,
IonIcon,
IonItem,
IonLabel,
IonList,
IonNote,
IonTitle,
IonToolbar,
} from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import { logoIonic } from 'ionicons/icons';
@Component({
selector: 'app-home',
template: `
<ion-header>
<ion-toolbar>
<ion-title>My app</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">My app</ion-title>
</ion-toolbar>
</ion-header>
<ion-list>
@for (item of items; track $index) {
<ion-item [button]="true" routerLink="/home/1">
<ion-icon slot="start" name="logo-ionic" size="large"></ion-icon>
<ion-label>Hello</ion-label>
<ion-note slot="end">6</ion-note>
</ion-item>
}
</ion-list>
</ion-content>
`,
imports: [
IonHeader,
IonToolbar,
IonContent,
IonTitle,
IonList,
IonItem,
IonLabel,
IonIcon,
IonNote,
RouterLink,
],
})
export default class HomeComponent {
items = new Array(50).fill(1);
constructor() {
addIcons({ logoIonic });
}
}

We are using a bunch of Ionic components here to create a standard mobile layout. Notice that since we are using standalone components, we just import whatever we want to use into the component and add it to the imports array.

import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'home',
loadComponent: () => import('./home/home.component'),
},
{
path: '',
pathMatch: 'full',
redirectTo: 'home',
},
];

Now if we serve the application we can immediately see some of Ionic’s magic come into play here with this condensed header. If I view this on an Android device I’m going to get the Android styling, but if I view it on an iOS device I am going to get this iOS design where we have a larger title on the page, and then as we scroll down that title sort of morphs into the top toolbar. You can easily see/test the iOS/Android specific styling by emulating difference devices using Chrome DevTools.

This gives us a bit of a sense of what Ionic’s UI components look like, but now let’s set up our second page so that we can see what a transition using the Ionic router outlet looks like.

import { Component } from '@angular/core';
import {
IonBackButton,
IonButtons,
IonContent,
IonHeader,
IonInputOtp,
IonTitle,
IonToolbar,
} from '@ionic/angular/standalone';
@Component({
selector: 'app-detail',
template: `
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button />
</ion-buttons>
<ion-title>Detail page</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-input-otp>
Didn't get a code? <a href="#">Resend the code</a>
</ion-input-otp>
</ion-content>
`,
imports: [
IonHeader,
IonToolbar,
IonContent,
IonTitle,
IonButtons,
IonBackButton,
IonInputOtp,
],
})
export default class DetailComponent {}
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'home',
loadComponent: () => import('./home/home.component'),
},
{
path: 'home/:id',
loadComponent: () => import('./detail/detail.component'),
},
{
path: '',
pathMatch: 'full',
redirectTo: 'home',
},
];

Now if you serve the application you will see that you can click on one of the list items and the detail page will animate into view just as it typically would for an iOS/Android application. Again, the animation will adapt appropriately for whatever platform it is running on.

Add Capacitor

The other key part that the Ionic CLI typically sets up for us is the integration with Capacitor which is what allows us to build our applications natively for iOS and Android and it also allows us to integrate various native capabilities (e.g. things like the Camera, Files, GPS, and so on).

We can also just manually add Capacitor to a standard Angular application by running a single command.

ionic integrations enable capacitor --quiet -- my-app io.ionic.starter

Now we can do everything we need with Capacitor (we will get more into Capacitor later).

Recap

That’s it! We have now converted a standard Angular application into an Ionic application that is almost identical to one created by the Ionic CLI.

    Why do we need to use ion-router-outlet instead of router-outlet?