All topics
Mobile · Learning hub

Ionic notes for developers

Master Ionic with a curated set of 1 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallTest yourself — Ionic quizMore Mobile notes
Ionic

Ionic Essentials

Ionic Essentials What Ionic Is Ionic is a UI component library and app toolkit for building apps that run in a web view — on iOS, Android, and the web — from a

Ionic Essentials

What Ionic Is

Ionic is a UI component library and app toolkit for building apps that run in a web view — on iOS, Android, and the web — from a single codebase. Instead of mapping to native platform widgets like React Native does, Ionic renders standard web technology (HTML, CSS, custom elements) inside a native shell, and ships a large set of prebuilt components (ion-button, ion-list, ion-modal, ion-tabs, and more) that already look and behave close to each platform's native conventions.

The native shell and the bridge to device APIs (camera, filesystem, geolocation, push notifications, etc.) is provided by Capacitor, Ionic's own native runtime. Ionic itself is framework-agnostic at the component level — the same ion-* elements work whether the app is built with Angular, React, or Vue, using a thin framework-specific integration package for each.

Because the UI runs in a web view, performance for complex animations or heavy native-feeling interactions can lag behind a fully native UI toolkit like SwiftUI or Jetpack Compose. In exchange, teams get a single codebase across three targets, direct reuse of web skills and libraries, and the ability to ship the same app as a PWA with no extra platform layer.

Components & Theming

Ionic components are registered as standard custom elements, so markup looks like plain HTML with ion- prefixed tags. Layout primitives (ion-grid, ion-row, ion-col), navigation shells (ion-header, ion-toolbar, ion-content), and interactive controls (ion-button, ion-input, ion-select) cover most app UI without reaching for extra libraries.

Theming is driven by CSS custom properties (variables). Overriding --ion-color-primary or a component's own --background/--color variables restyles it consistently across iOS and Android rendering modes, without touching component internals or fighting specificity.

<!-- variables.css — global theme overrides -->
:root {
  --ion-color-primary: #6d28d9;
  --ion-color-primary-rgb: 109, 40, 217;
  --ion-color-primary-shade: #6021b3;
  --ion-color-primary-tint: #7c3ce0;
}

<!-- usage in a page template -->
<ion-header>
  <ion-toolbar color="primary">
    <ion-title>Orders</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
  <ion-list>
    <ion-item-sliding *ngFor="let order of orders">
      <ion-item>
        <ion-label>
          <h2>{{ order.customer }}</h2>
          <p>{{ order.total | currency }}</p>
        </ion-label>
        <ion-badge slot="end" [color]="order.status === 'paid' ? 'success' : 'warning'">
          {{ order.status }}
        </ion-badge>
      </ion-item>
      <ion-item-options side="end">
        <ion-item-option color="danger" (click)="cancel(order)">Cancel</ion-item-option>
      </ion-item-options>
    </ion-item-sliding>
  </ion-list>
</ion-content>

Capacitor Plugins & the Native Bridge

Capacitor exposes native device capabilities through plugins — official ones (Camera, Geolocation, Filesystem, Push Notifications, Preferences) and community or custom plugins that follow the same pattern. Each plugin is a small TypeScript API on the JS side backed by native Swift/Kotlin implementations; calls cross the bridge asynchronously and return promises.

Unlike Cordova, Capacitor treats the native ios/ and android/ projects as source you own and can open directly in Xcode or Android Studio, similar to Expo's bare workflow. Adding a plugin that touches native code requires running npx cap sync to copy web assets and update native dependencies, then rebuilding the native project.

import { Camera, CameraResultType, CameraSource } from '@capacitor/camera'
import { Preferences } from '@capacitor/preferences'
import { Geolocation } from '@capacitor/geolocation'

async function scanReceipt() {
  const photo = await Camera.getPhoto({
    quality: 80,
    resultType: CameraResultType.Uri,
    source: CameraSource.Camera,
  })
  return photo.webPath
}

async function rememberLastLocation() {
  const position = await Geolocation.getCurrentPosition({ enableHighAccuracy: true })
  await Preferences.set({
    key: 'lastLocation',
    value: JSON.stringify({ lat: position.coords.latitude, lng: position.coords.longitude }),
  })
}

// capacitor.config.ts
import type { CapacitorConfig } from '@capacitor/cli'

const config: CapacitorConfig = {
  appId: 'com.mycompany.orders',
  appName: 'Orders',
  webDir: 'dist',
  plugins: {
    SplashScreen: { launchAutoHide: false },
    PushNotifications: { presentationOptions: ['badge', 'sound', 'alert'] },
  },
}

export default config

// After adding/updating a native plugin:
// npx cap sync

Framework Integration: Angular, React & Vue

The same ion-* custom elements are used regardless of framework, but each framework gets a thin wrapper package that adapts Ionic's imperative APIs (navigation, overlays) to that framework's idioms. Angular apps typically use IonicModule and Angular Router with Ionic's RouterOutlet-aware navigation; React apps use @ionic/react with IonReactRouter and hooks like useIonRouter; Vue apps use @ionic/vue with vue-router integration.

// React — @ionic/react
import { IonApp, IonRouterOutlet, IonTabs, IonTabBar, IonTabButton, IonIcon, IonLabel } from '@ionic/react'
import { IonReactRouter } from '@ionic/react-router'
import { Route, Redirect } from 'react-router-dom'
import { home, cart } from 'ionicons/icons'
import HomePage from './pages/HomePage'
import CartPage from './pages/CartPage'

export default function App() {
  return (
    <IonApp>
      <IonReactRouter>
        <IonTabs>
          <IonRouterOutlet>
            <Route exact path="/home" component={HomePage} />
            <Route exact path="/cart" component={CartPage} />
            <Redirect exact path="/" to="/home" />
          </IonRouterOutlet>
          <IonTabBar slot="bottom">
            <IonTabButton tab="home" href="/home">
              <IonIcon icon={home} />
              <IonLabel>Home</IonLabel>
            </IonTabButton>
            <IonTabButton tab="cart" href="/cart">
              <IonIcon icon={cart} />
              <IonLabel>Cart</IonLabel>
            </IonTabButton>
          </IonTabBar>
        </IonTabs>
      </IonReactRouter>
    </IonApp>
  )
}

Platform-Specific Styling & Adaptive UI

Ionic ships two rendering modes, ios and md, chosen automatically from the running platform (overridable per app). The mode changes default component look and feel — transitions, tab bar placement, back-button chevron vs label — so the same markup adapts to each platform's conventions without separate templates.

For styling that must diverge further, Ionic exposes the current mode as a class on the document (ios or md) and as a global $ionic-mode variable in Sass, plus a mode-specific CSS attribute selector on individual components. Reach for these only when the default per-mode component styling genuinely isn't enough — most apps should stay within the shared theme variables to keep both platforms visually consistent.

Common Pitfalls

  • Forgetting to run npx cap sync after installing or updating a native plugin — the web build alone does not update the native ios/android projects, so the new plugin silently fails at runtime.

  • Testing exclusively in a desktop browser: browser DevTools can approximate a mobile viewport but cannot exercise real Capacitor plugin behavior (camera, push, biometrics) — use ionic cap run ios/android on a simulator or device for that.

  • Overriding component internals with deep CSS selectors instead of the documented CSS custom properties — internal DOM structure can change between Ionic versions and silently break unsupported overrides.

  • Mixing ion-router-outlet navigation with the underlying framework router in ways that fight each other — let IonReactRouter/Ionic's Angular routing own transitions rather than calling the framework router directly for tab or stack navigation.

  • Shipping without testing both ios and md rendering modes — a layout that looks right in one mode can overflow or misalign in the other, especially around headers, tab bars, and modals.

  • Bundling large third-party JS libraries without checking their impact on initial load — since Ionic apps ship a full web bundle to a web view, bundle size directly affects perceived startup performance on lower-end devices.

Keep your Ionic knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever