Back to Ionic Framework

Migrating v8 to v9

packages/migrate/docs/v9.md

9.0.06.3 KB
Original Source

Migrating v8 to v9

This page covers what npx @ionic/migrate handles for the Ionic 8 to Ionic 9 upgrade, and what it leaves to you. The full list of breaking changes, with before/after examples, is in the Ionic 9 upgrade guide. Every finding the tool prints links to the section of that guide it came from.

Coverage

Find your framework below, then read the all frameworks section as well. Those changes come from @ionic/core, so they apply on top of the framework-specific ones, and they are the whole list for a vanilla app.

experimental migrations only run with --experimental.

Angular

ChangeMode
@ionic/angular -> @ionic/angular/lazy, /standalone -> @ionic/angularauto
@ionic/angular package bumpauto
moduleResolution: "node" -> "bundler" in tsconfig*.jsonauto
TypeScript raised to the 5.4 floorauto
CSS ~ prefix removal in @ionic/angular importsauto
Add provideZoneChangeDetection() to a standalone bootstrap (keep Zone.js)auto
NgModule bootstrap zone providerreport
IonicModule deprecation (provideIonicAngular())report
Angular below the 18 floorreport
Angular 22's OnPush default and its Node floorreport
@ionic/angular-toolkit version bumpreport
browserslist entries below Angular 20+'s own browser policyreport
browserslist entries raised to Angular's policy floorsexperimental

React

ChangeMode
@ionic/react + React 18 + React Router v6 bumps, drop @types/react-router*auto
TypeScript raised to the 5.4 floorauto
<Route exact> removal, component={X} -> element={<X />}auto
React Router v6: removed imports, IonRedirect, render/non-identifier component, route children, history prop, regex pathsreport
useIonModal/useIonPopover calls whose componentProps no longer type checkreport
An inline overlay component's unannotated props parameterexperimental

Vue

ChangeMode
@ionic/vue + Vue Router 5 + Vue 3.5 bumpsauto
next() in navigation guardsreport

All frameworks

ChangeMode
@ionic/core package bumpauto
autocorrect="off" on ion-input/ion-searchbarauto
browserslist entries raised to the v9 browser floorsauto
Browsers from the v9 list a browserslist doesn't namereport
Legacy picker (ion-picker-legacy, pickerController, removed types)report
ion-img deprecationreport
ion-nav router removal (setRouteId/getRouteId/updateURL)report
@ionic/core imports outside the new exports allowlistreport
Capacitor below version 7 (Ionic 9's minimum) and the Capacitor 2 native-detection changereport
ion-input/ion-textarea/ion-select internal DOM and shadow part changesreport
label-placement="floating" with slotted start/end contentreport
ion-textarea md min-height 56px -> 72pxreport
ion-modal handleBehavior default ("none" -> "cycle")report
ion-select ionChange firing and the action sheet selected rolereport
swipeBackEnabled config, now read once at outlet mountreport

What you check by hand

The tool can't point at the code these changes affect, so check them against the upgrade guide yourself:

  • React Router v6 needs a /* suffix on any route whose element contains nested routes or a child IonRouterOutlet (path="/tabs" -> path="/tabs/*"). Knowing which routes those are means resolving each element back to what it renders, so flagging every route without a suffix would be noise.
  • Angular 22 defaults components to OnPush, so state mutated as a plain field in an Ionic lifecycle hook stops re-rendering. The report flags Angular 22 in package.json, but it doesn't find the affected components - ng update has a migration for that.
  • Overlay hook calls whose component the type checker can't resolve. The tool runs without your tsconfig.json, so a component behind a paths alias (@/components/Modal), or one typed as React.FC with @types/react uninstalled, is skipped rather than guessed at, and a build error can still be waiting in one. A mismatch that only exists under strict is skipped too, so that everything it does report is a real error.

Notes on individual migrations

  • Angular zoneless is auto-fixed only for the standalone bootstrapApplication shape. NgModule apps are flagged for manual migration instead. Neither fires unless the app loads Zone.js, since there is nothing to preserve otherwise and the provider fails to bootstrap without it.
  • The overlay hook prop changes are report-only: a prop the component doesn't declare is either a typo or a prop it should have declared, and a missing required prop has no value to supply. The exception is a component written inline at the call, whose props parameter --experimental annotates from the componentProps object literal it is passed. That is a guess at intent ({ name: 'Dave' } gives { name: string }), and it declines anything but a literal, or props that don't cover every name the component reads.
  • The component DOM/shadow-part changes are report-only. The right replacement depends on what the CSS rule was doing, and ion-select's part="inner" has none at all.
  • A browserslist list is read from .browserslistrc, a browserslist file, or the package.json field. A query-style list (last 2 versions, > 0.5%) names no browser, so nothing in it is raised or reported.
  • Angular's own browser policy, which the CLI enforces from Angular 20 on, is read from the installed @angular/build and resolved with the project's browserslist. Without node_modules the report names the policy but not the versions. The two things it reads are Angular internals (a .browserslistrc in the package on 20, a BASELINE_DATE constant on 21+), so when a new Angular major lands, check the reported floors against the ng build warning before trusting them.
  • Angular's moduleResolution fix skips a tsconfig whose module is CommonJS. TypeScript rejects bundler resolution there, and a Node-side config doesn't resolve @ionic/angular subpaths anyway.