Guide to using Ionic Framework components for beautiful native-looking Capacitor apps. Covers component usage, theming, platform-specific styling, and best practices for mobile UI...
TRIGGER when:
Ion* / ion-* components in a Capacitor app--ion-color-*, dark mode, high contrast, per-platform stylesmode overrides misbehavenpm i @ionic/*@latestDo not use:
konsta-uitailwind-capacitorsafe-area-handlingcapacitor-keyboard (Ionic apps use resize: 'ionic')ionic-appflow-migration, ionic-enterprise-sdk-migration| Package | Current | Notes |
|---|---|---|
@ionic/core, @ionic/react, @ionic/vue, @ionic/angular |
9.x (latest) |
8.x on v8-lts tag |
ionicons |
8.x | |
| Ionic 9 minimums | Capacitor 7+, iOS 16+, Android Chromium 89+, Angular 18-22, React 18/19 + React Router 6.4+, Vue 3.5 + vue-router 5, TypeScript 5.4+ |
Check installed versions first: npm ls @ionic/core @ionic/react @ionic/vue @ionic/angular. Write code for the installed major; do not mix v8 and v9 APIs.
setupIonicReact / IonicVue / provideIonicAngular config, capacitor.config Keyboard.resize.npx @ionic/migrate in preview first, report findings, then apply.Only load a reference when its topic is in play.
IonPage > IonHeader + IonContent (+ optional IonFooter). Missing IonPage breaks transitions and lifecycle hooks.ion-content. Do not make body or a wrapper div scroll; use IonContent scrollToTop()/getScrollElement().ion-router-outlet (IonRouterOutlet, IonTabs) for URL routing. In Ionic 9 ion-nav no longer integrates with ion-router; keep it for local URL-less stacks inside a page.--ion-safe-area-top|right|bottom|left (default env()); do not add body padding on top. Requires viewport-fit=cover.label + labelPlacement on the control itself (<IonInput label="Email" labelPlacement="floating" />). Legacy <IonItem><IonLabel/><IonInput/></IonItem> labeling was removed in Ionic 8.<IonModal isOpen> / trigger or hooks (useIonModal, modalController) consistently; always handle didDismiss to reset state..ios / .md classes on html or mode CSS selectors; use isPlatform('ios') only for behavior, never navigator.userAgent.import { heart } from 'ionicons/icons') and pass icon={heart}; string names need addIcons registration in standalone/bundled builds.npm run build && npx cap sync.?ionic:mode=ios and ?ionic:mode=md to the dev URL; check headers, toolbars, lists, buttons.ion-palette-dark to <html> if using the class palette); check contrast of custom colors.grep -rn "ion-picker-legacy\|pickerController\|useIonPicker\|@ionic/angular/standalone\|autocorrect=\"off\"" src/ returns nothing; npx tsc --noEmit passes.| Error / symptom | Cause | Fix |
|---|---|---|
Package path ./dist/... is not exported from package (webpack) / ERR_PACKAGE_PATH_NOT_EXPORTED (Node) for @ionic/core |
Ionic 9 exports map; deep import |
Import from @ionic/core, @ionic/core/components, @ionic/core/components/ion-*.js, @ionic/core/loader, @ionic/core/css/*.css |
Import errors or migrate warnings for @ionic/angular/standalone (Ionic 9) |
Standalone imports moved | Import from @ionic/angular; lazy APIs from @ionic/angular/lazy |
React: Property 'component' does not exist on Route / IonRoute |
Ionic 9 requires React Router 6 | element={<Page />}, Navigate instead of Redirect, useNavigate instead of useHistory |
| Autocorrect turned ON after upgrade | autocorrect="off" string is truthy in Ionic 9 |
Remove the attribute or bind false |
ionChange no longer fires on re-selecting same option |
Ionic 9 fires only on value change | Use ionDismiss for dismissal |
| Sheet modal handle now focusable / cycles breakpoints | Ionic 9 handleBehavior default cycle |
handle-behavior="none" to restore |
| Styles missing, components unstyled | Core CSS not imported | Import @ionic/<fw>/css/core.css (+ structure/typography) once at entry |
| Dark mode never activates | No palette imported | Import css/palettes/dark.system.css (or .class.css + ion-palette-dark class) |
| Angular 21 view not updating after async work | Zoneless default in Ionic 9 + Angular 21 | Use signals / markForCheck(), or add provideZoneChangeDetection() |