Why Ionic Needs Hybrid-Aware Rules
Ionic occupies a unique space: it's a web-based framework that deploys as native apps via Capacitor. Your code is web tech (React, Angular, or Vue) rendered in a native WebView with access to native device APIs through Capacitor plugins. AI assistants don't understand this hybrid model — they generate either pure web code (no native API access) or React Native patterns (wrong rendering model).
The most common AI failures: using browser-only APIs (window.localStorage) instead of Capacitor Preferences plugin, generating React Native components (View, Text) instead of Ionic components (IonContent, IonList), ignoring platform-adaptive styling, using React Router instead of IonRouter, and missing Capacitor plugin setup for native features.
These rules target Ionic 8+ with Capacitor 6+ and React (adjust for Angular or Vue). The core Ionic component and Capacitor patterns are the same regardless of the UI framework.
Rule 1: Ionic UI Components, Not Plain HTML
The rule: 'Use Ionic's UI components for all mobile UI: IonPage as the root of every page, IonHeader + IonToolbar + IonTitle for headers, IonContent for scrollable content, IonList + IonItem for lists, IonButton for buttons, IonInput for text inputs, IonCard for cards, IonModal for modals. Ionic components automatically adapt to iOS and Android design conventions.'
For navigation: 'Use IonRouter (React) or ion-router-outlet (Angular) — not React Router's BrowserRouter directly. IonRouterOutlet provides native-feeling page transitions (slide animation on iOS, fade on Android). Use IonTabs for tab-based navigation. Use IonMenu for side menus. Use useIonRouter() for programmatic navigation.'
AI generates plain HTML (div, button, input) in Ionic projects. Ionic components provide: adaptive styling (iOS vs Material Design), native-feeling animations, accessibility attributes, and platform-consistent behavior. Plain HTML looks and feels wrong on mobile.
- IonPage → IonHeader → IonContent structure on every page
- IonList + IonItem for lists — IonCard for cards — IonButton for actions
- IonRouterOutlet for navigation — IonTabs for tabs — IonMenu for drawer
- Ionic components auto-adapt: iOS styling on iOS, Material on Android
- Never plain HTML elements — Ionic components handle accessibility + animations
AI generates <div> and <button> in Ionic. Ionic components (IonButton, IonList, IonCard) auto-adapt to iOS and Android styling, provide native animations, and handle accessibility. Plain HTML looks and feels wrong on mobile.
Rule 2: Capacitor for All Native Access
The rule: 'Use Capacitor plugins for all native device access. @capacitor/camera for camera/photos. @capacitor/filesystem for file access. @capacitor/preferences for key-value storage (replaces localStorage for native persistence). @capacitor/geolocation for GPS. @capacitor/push-notifications for push. @capacitor/share for native share sheet. Never use browser APIs for features that have Capacitor equivalents.'
For Capacitor usage: 'Import from @capacitor/[plugin]: import { Camera, CameraResultType } from "@capacitor/camera". Call native APIs: const photo = await Camera.getPhoto({ resultType: CameraResultType.Uri }). Capacitor APIs are async and return Promises. Check Capacitor.isNativePlatform() before calling native-only APIs — provide web fallbacks for PWA.'
For custom native code: 'Use Capacitor custom plugins for native functionality not covered by official plugins. Define the plugin interface in TypeScript, implement in Swift (iOS) and Kotlin (Android). Register with registerPlugin(). This is cleaner than React Native's native modules — a typed interface with platform implementations.'
localStorage works in a WebView but doesn't persist reliably on native. Capacitor Preferences plugin does. For every browser API, check if a Capacitor plugin exists — it handles native edge cases that browser APIs don't.
Rule 3: Platform Detection and Adaptive Code
The rule: 'Use Ionic's isPlatform() for platform detection: isPlatform("ios"), isPlatform("android"), isPlatform("capacitor") (native), isPlatform("mobileweb") (browser on mobile). Use Capacitor.isNativePlatform() for native vs web. Provide web fallbacks for Capacitor features when running as a PWA. Use platform-specific styles with Ionic's mode attribute: mode="ios" or mode="md".'
For adaptive behavior: 'Detect platform once at app initialization — store in context/state. Use it for: feature availability (push notifications only on native), UI adjustments (safe areas on iOS), and API selection (Capacitor camera on native, file input on web). Never assume native — always check and provide fallbacks.'
For testing: 'Test on: iOS simulator, Android emulator, Chrome (PWA mode), and Safari (iOS PWA). Each platform has different behavior for: camera access, file storage, push notifications, and deep linking. Use Capacitor's live reload for native testing: npx cap run ios --livereload.'
- isPlatform('ios'), isPlatform('android'), isPlatform('capacitor')
- Capacitor.isNativePlatform() for native vs web detection
- Web fallbacks for all Capacitor features — PWA must work without native
- Platform-specific UI: safe areas on iOS, back button on Android
- Test on all targets: iOS sim, Android emu, Chrome PWA, Safari PWA
Ionic apps can run as PWAs — don't assume native. Check Capacitor.isNativePlatform() and provide web fallbacks. Camera becomes file input. Push notifications become in-app notifications. Preferences falls back to localStorage.
Rule 4: Hybrid App Architecture
The rule: 'Structure as a standard web app with a native access layer. src/ for all app code (components, pages, services). capacitor.config.ts for Capacitor configuration. ios/ and android/ for native projects (auto-generated — don't edit manually for most changes). Use a service layer that abstracts native/web differences: StorageService uses Capacitor Preferences on native, localStorage on web.'
For offline support: 'Ionic apps can work offline because they're web apps in a native shell. Use Capacitor Preferences for offline data. Use Service Workers for offline asset caching. Use a sync queue for offline mutations that sync when connectivity returns. Test offline behavior by toggling airplane mode on a device.'
For builds: 'Use npx cap sync after every dependency change (copies web assets to native projects). Use npx cap open ios to open in Xcode, npx cap open android for Android Studio. Build web first (npm run build), then sync, then build native. Use Appflow or EAS for CI builds.'
Rule 5: Ionic-Specific Patterns
The rule: 'Use Ionic lifecycle hooks: ionViewWillEnter (page becomes active), ionViewDidLeave (page leaves). These replace useEffect for page-level setup/teardown in Ionic — they fire on navigation, not on mount/unmount (Ionic caches pages for back-navigation performance). Use IonLoading for loading indicators, IonToast for notifications, IonActionSheet for action menus, IonAlert for confirmations.'
For forms: 'Use IonInput, IonSelect, IonToggle, IonCheckbox, IonDatetime for form controls — they adapt to platform conventions automatically. Pair with React Hook Form or Formik for form state management. Use IonItem as the form field wrapper — it provides consistent spacing, labels, and error display.'
For theming: 'Use CSS custom properties for theming: --ion-color-primary, --ion-background-color. Define in theme/variables.css. Use Ionic's color system: color="primary", color="danger", color="success" on components. Support dark mode with prefers-color-scheme media query — Ionic supports it out of the box.'
- ionViewWillEnter/ionViewDidLeave — not useEffect for page lifecycle
- IonLoading, IonToast, IonActionSheet, IonAlert for native-feeling overlays
- Ionic form controls: IonInput, IonSelect, IonToggle — auto-adapt to platform
- CSS custom properties for theming: --ion-color-primary, --ion-background-color
- Dark mode via prefers-color-scheme — Ionic supports it natively
Complete Ionic Rules Template
Consolidated rules for Ionic 8+ with Capacitor 6+ projects.
- Ionic UI components: IonPage, IonContent, IonList, IonButton — never plain HTML
- IonRouterOutlet for navigation — IonTabs for tabs — useIonRouter for programmatic
- Capacitor plugins for native: camera, filesystem, preferences, geolocation, push
- isPlatform() for detection — web fallbacks for all native features — test all platforms
- Service layer abstracts native/web: StorageService, CameraService with platform check
- ionViewWillEnter lifecycle — IonLoading/IonToast for overlays — Ionic form controls
- npx cap sync after deps — npx cap open for native IDE — build web first then sync
- CSS custom properties for theming — dark mode support built-in — Cypress for E2E