Docs
/
Angular
Chapter 21
21 — Ionic Mobile (Angular + Capacitor)
What is Ionic?
Ionic is a cross-platform UI framework that lets you build mobile (iOS/Android) and web apps from a single Angular codebase. Capacitor bridges web code to native APIs.
Angular App → Ionic UI Components → Capacitor → Native iOS/Android
| Ionic + Capacitor | React Native | Flutter | |
|---|---|---|---|
| Language | TypeScript/HTML/CSS | JavaScript/JSX | Dart |
| UI | Web components (styled native-like) | Native components | Custom rendering |
| Performance | Good (WebView) | Good (native bridge) | Excellent (compiled) |
| Code sharing | 90%+ with web | ~70% with web | Separate from web |
| Learning curve | Low (if you know Angular) | Medium | Medium |
Setup
# Install Ionic CLI
npm install -g @ionic/cli
# Create Ionic + Angular app
ionic start my-app blank --type=angular --capacitor
# Or add Ionic to existing Angular app
ng add @ionic/angular
npm install @capacitor/core @capacitor/cli
npx cap init my-app com.example.myapp
Project Structure
my-app/
├── src/
│ ├── app/
│ │ ├── tabs/ # Tab-based navigation
│ │ │ ├── tabs.page.ts
│ │ │ └── tabs.routes.ts
│ │ ├── home/
│ │ │ ├── home.page.ts
│ │ │ └── home.page.html
│ │ ├── profile/
│ │ ├── settings/
│ │ └── services/
│ ├── theme/
│ │ └── variables.scss # Ionic CSS variables (colors, fonts)
│ └── global.scss
├── android/ # Native Android project (auto-generated)
├── ios/ # Native iOS project (auto-generated)
├── capacitor.config.ts
├── ionic.config.json
└── package.json
Ionic Components
<!-- Header -->
<ion-header>
<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-back-button defaultHref="/home" />
</ion-buttons>
<ion-title>Profile</ion-title>
<ion-buttons slot="end">
<ion-button (click)="openSettings()">
<ion-icon name="settings-outline" />
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<!-- Content with pull-to-refresh -->
<ion-content>
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
<ion-refresher-content />
</ion-refresher>
<!-- List -->
<ion-list>
@for (item of items; track item.id) {
<ion-item-sliding>
<ion-item [routerLink]="['/items', item.id]">
<ion-avatar slot="start">
<img [src]="item.avatar" />
</ion-avatar>
<ion-label>
<h2>{{ item.name }}</h2>
<p>{{ item.description }}</p>
</ion-label>
<ion-badge slot="end" color="success">{{ item.status }}</ion-badge>
</ion-item>
<!-- Swipe actions -->
<ion-item-options side="end">
<ion-item-option color="danger" (click)="delete(item)">
<ion-icon name="trash" slot="icon-only" />
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
}
</ion-list>
<!-- Infinite scroll -->
<ion-infinite-scroll (ionInfinite)="loadMore($event)">
<ion-infinite-scroll-content loadingSpinner="circles" />
</ion-infinite-scroll>
</ion-content>
<!-- Tabs -->
<ion-tabs>
<ion-tab-bar slot="bottom">
<ion-tab-button tab="home">
<ion-icon name="home-outline" />
<ion-label>Home</ion-label>
</ion-tab-button>
<ion-tab-button tab="search">
<ion-icon name="search-outline" />
<ion-label>Search</ion-label>
</ion-tab-button>
<ion-tab-button tab="profile">
<ion-icon name="person-outline" />
<ion-label>Profile</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
Capacitor — Native API Access
# Add native platforms
npx cap add android
npx cap add ios
# Sync web assets to native
npx cap sync
# Open in native IDE
npx cap open android # Opens Android Studio
npx cap open ios # Opens Xcode
# Live reload on device
ionic cap run android --livereload --external
ionic cap run ios --livereload --external
Camera
npm install @capacitor/camera
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
async takePhoto() {
const image = await Camera.getPhoto({
quality: 80,
resultType: CameraResultType.Base64,
source: CameraSource.Camera, // Or CameraSource.Photos for gallery
allowEditing: false,
});
this.photoBase64 = `data:image/jpeg;base64,${image.base64String}`;
}
Geolocation
npm install @capacitor/geolocation
import { Geolocation } from '@capacitor/geolocation';
async getLocation() {
const pos = await Geolocation.getCurrentPosition();
this.lat = pos.coords.latitude;
this.lng = pos.coords.longitude;
}
// Watch position
const watchId = await Geolocation.watchPosition({}, (pos) => {
if (pos) {
console.log(pos.coords.latitude, pos.coords.longitude);
}
});
// Stop watching
await Geolocation.clearWatch({ id: watchId });
Local Storage (Preferences)
npm install @capacitor/preferences
import { Preferences } from '@capacitor/preferences';
// Set
await Preferences.set({ key: 'token', value: 'abc123' });
// Get
const { value } = await Preferences.get({ key: 'token' });
// Remove
await Preferences.remove({ key: 'token' });
// Clear all
await Preferences.clear();
Push Notifications
npm install @capacitor/push-notifications
import { PushNotifications } from '@capacitor/push-notifications';
async registerPush() {
const perm = await PushNotifications.requestPermissions();
if (perm.receive === 'granted') {
await PushNotifications.register();
}
PushNotifications.addListener('registration', token => {
console.log('Push token:', token.value);
// Send token to your backend
});
PushNotifications.addListener('pushNotificationReceived', notification => {
console.log('Received:', notification);
});
PushNotifications.addListener('pushNotificationActionPerformed', action => {
console.log('Tapped notification:', action.notification);
// Navigate based on notification data
});
}
Platform Detection
import { Platform } from '@ionic/angular';
@Component({ ... })
export class AppComponent {
private platform = inject(Platform);
isIos = this.platform.is('ios');
isAndroid = this.platform.is('android');
isMobile = this.platform.is('mobile');
isDesktop = this.platform.is('desktop');
isPwa = this.platform.is('pwa');
}
@if (isIos) {
<p>iOS-specific content</p>
}
Theming
// src/theme/variables.scss
:root {
--ion-color-primary: #3880ff;
--ion-color-secondary: #3dc2ff;
--ion-color-tertiary: #5260ff;
--ion-color-success: #2dd36f;
--ion-color-warning: #ffc409;
--ion-color-danger: #eb445a;
--ion-font-family: 'Inter', sans-serif;
}
// Dark mode
@media (prefers-color-scheme: dark) {
:root {
--ion-background-color: #1a1a2e;
--ion-text-color: #ffffff;
--ion-color-primary: #6c63ff;
}
}
App Store Deployment
# Build web assets
ionic build --prod
# Sync to native projects
npx cap sync
# Android: Open Android Studio → Build → Generate Signed APK/Bundle
npx cap open android
# iOS: Open Xcode → Product → Archive → Distribute
npx cap open ios
Key Takeaways
- Ionic uses web technologies inside a native WebView — share 90%+ code with your web app
- Capacitor bridges to native APIs (camera, GPS, push, filesystem, biometrics)
- Use
ionic cap runwith--livereloadfor fast development on real devices - Ionic components automatically adapt to iOS/Android styling (Material on Android, Cupertino on iOS)
- Use
Platform.is()for platform-specific behavior - Store sensitive data with
@capacitor/preferences(encrypted on native) - For performance-critical rendering (games, AR), consider native frameworks instead