Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c1dab35
chore(deps): bump @stencil/core to 4.43.4
ShaneK May 22, 2026
d6ac009
feat(input,searchbar)!: change autocorrect prop to boolean
ShaneK May 22, 2026
0c637b6
feat(angular)!: support Angular 21, drop Angular 16 and 17
ShaneK May 22, 2026
e7e54b5
fix(angular): update test tooling for Angular 18+ minimum
ShaneK May 26, 2026
53a9122
chore(angular): tighten ng21 tsconfig and document zoneless opt-out
ShaneK May 28, 2026
034efd5
chore(git): merge
ShaneK May 29, 2026
0bd395b
chore(angular): handle errors in clean-generated and update browsersl…
ShaneK May 29, 2026
74870ab
Merge branch 'major-9.0' of github.com:ionic-team/ionic-framework int…
ShaneK Jun 1, 2026
4de5689
chore(deps): bump react and vue test apps to typescript 5.9
ShaneK Jun 1, 2026
8c33698
chore(deps): bump @stencil/core to 4.43.5
ShaneK Jun 1, 2026
5d5b91b
docs(breaking): document breaking Angular 21 changes
ShaneK Jun 2, 2026
3dc3aef
fix(angular): correct ng21 import order and remove conflicting angula…
ShaneK Jun 2, 2026
df2bd72
style(react): format generated components.ts with prettier
ShaneK Jun 2, 2026
d5154cd
docs: better language
ShaneK Jun 3, 2026
3fdc66b
fix(react): pass nested overlay consumer render prop as children for …
ShaneK Jun 4, 2026
87e5eac
fix(react): externalize react and react-dom subpath imports in rollup…
ShaneK Jun 4, 2026
c7e2519
fix(angular): restore narrow per-component event types via output-tar…
ShaneK Jun 5, 2026
0b27473
docs(breaking): split input and searchbar autocorrect into separate s…
ShaneK Jun 5, 2026
1fe8a2a
Merge branch 'feat/angular-21-support' of github.com:ionic-team/ionic…
ShaneK Jun 5, 2026
25e753f
docs(breaking): fixing alphabetical order
ShaneK Jun 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ jobs:
strategy:
fail-fast: false
matrix:
apps: [ng16, ng17, ng18, ng19, ng20]
apps: [ng18, ng19, ng20, ng21]
needs: [build-angular, build-angular-server]
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/stencil-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ jobs:
strategy:
fail-fast: false
matrix:
apps: [ng16, ng17, ng18, ng19, ng20]
apps: [ng18, ng19, ng20, ng21]
needs: [build-angular, build-angular-server]
runs-on: ubuntu-latest
steps:
Expand Down
87 changes: 87 additions & 0 deletions BREAKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ This is a comprehensive list of the breaking changes introduced in the major ver
- [Legacy Picker](#version-9x-legacy-picker)
- [Router Outlet](#version-9x-router-outlet)
- [Select](#version-9x-select)
- [Input and Searchbar](#version-9x-input-searchbar)
Comment thread
brandyscarney marked this conversation as resolved.
Outdated
- [Framework Specific](#version-9x-framework-specific)
- [Angular](#version-9x-angular)
- [React](#version-9x-react)
- [Vue](#version-9x-vue)

Expand All @@ -31,7 +33,9 @@ This section details the desktop browser, JavaScript framework, and mobile platf
**Minimum JavaScript Framework Versions**
| Framework | Supported Version |
| --------- | --------------------- |
| Angular | 18+ |
| React | 18+ |
| Vue | 3.5+ |

<h2 id="version-9x-package-exports">Package Exports</h2>

Expand Down Expand Up @@ -97,8 +101,91 @@ The `ionChange` event on `ion-select` now only fires when the selected value act

Apps that relied on `ionChange` firing on every confirmation (for example, to detect overlay dismissal without a value change) should listen for `ionDismiss` instead, or use the `didDismiss` event on the underlying alert or action sheet.

<h4 id="version-9x-input-searchbar">Input and Searchbar</h4>

The `autocorrect` property on `ion-input` and `ion-searchbar` is now a `boolean` and defaults to `false`. It was previously typed as `'on' | 'off'` with a default of `'off'`. This resolves a type conflict introduced when TypeScript 5.9 added `autocorrect: boolean` to the DOM `HTMLElement` interface.

The string form no longer behaves the same way. Because an HTML attribute coerces to `true` for any non-empty string, `autocorrect="off"` now evaluates to `true` (autocorrect enabled). Migrate to the boolean property:

- Remove the attribute to keep autocorrect disabled (the default).
- Use a property binding to enable it: `[autocorrect]="true"` (Angular), `autocorrect={true}` (React), or `:autocorrect="true"` (Vue).

<h2 id="version-9x-framework-specific">Framework Specific</h2>

<h4 id="version-9x-angular">Angular</h4>

**Minimum Angular Version**

Ionic 9 requires Angular 18 or later. Angular 16 and 17 are no longer supported.

**Angular 21 Requires Explicit Zone Change Detection**

Angular 21 defaults `bootstrapModule()` and `bootstrapApplication()` to zoneless change detection. `zone.js` in your polyfills is ignored unless you opt back in explicitly, which surfaces as runtime `NG0909` errors and breaks change detection for asynchronous updates (modal and popover lifecycle, tab navigation, and anything depending on async-resolved state). Ionic 9 relies on zone-based change detection, so apps on Angular 21 must provide it explicitly.

Standalone bootstrap:

```diff
import { bootstrapApplication } from '@angular/platform-browser';
+ import { provideZoneChangeDetection } from '@angular/core';

bootstrapApplication(AppComponent, {
providers: [
+ provideZoneChangeDetection(),
// ...other providers
],
});
```

NgModule bootstrap:

```diff
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+ import { provideZoneChangeDetection } from '@angular/core';

platformBrowserDynamic()
- .bootstrapModule(AppModule)
+ .bootstrapModule(AppModule, {
+ applicationProviders: [provideZoneChangeDetection()],
+ })
.catch((err) => console.error(err));
```

Angular forbids `provideZoneChangeDetection()` inside an NgModule's `providers` array, so for NgModule apps it must be passed as `applicationProviders` on the `bootstrapModule()` call. This step is only required on Angular 21. Angular 18 through 20 are unaffected.

**TypeScript**

Ionic 9 supports TypeScript 5.4 or later, matching the minimum for Angular 18. Angular 21 requires TypeScript 5.9 or later per Angular's own requirements.

**Module Resolution**

`@ionic/angular` is now published with `exports`-based subpath resolution. Apps using TypeScript `moduleResolution: "node"` (classic) can fail to resolve subpaths such as `@ionic/angular/standalone`. Set `moduleResolution` to `"bundler"` (the default for `ng new` on Angular 17 and later). Refer to [Package Exports](#version-9x-package-exports).

**CSS Imports No Longer Use the `~` Prefix**

Angular's current build pipeline no longer supports the webpack-loader `~` prefix in CSS `@import` statements:

```diff
- @import '~@ionic/angular/css/core.css';
+ @import '@ionic/angular/css/core.css';
```

**Narrowed Event Types**

The Angular output target no longer surfaces the narrow `*CustomEvent` types (such as `RefresherCustomEvent` and `ReorderEndCustomEvent`) through template type inference. Use `CustomEvent<*EventDetail>` and cast `event.target` at call sites that invoke methods on it:

```diff
- import { RefresherCustomEvent } from '@ionic/angular';
-
- onRefresh(event: RefresherCustomEvent) {
- event.target.complete();
- }
+ import type { RefresherEventDetail } from '@ionic/core';
+
+ onRefresh(event: CustomEvent<RefresherEventDetail>) {
+ (event.target as HTMLIonRefresherElement | null)?.complete();
+ }
```

<h4 id="version-9x-react">React</h4>

The `@ionic/react-router` package now requires React Router v6. React Router v5 is no longer supported.
Expand Down
6 changes: 3 additions & 3 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ ion-infinite-scroll-content,prop,loadingText,IonicSafeString | string | undefine
ion-input,scoped
ion-input,prop,autocapitalize,string,'off',false,false
ion-input,prop,autocomplete,"additional-name" | "address-level1" | "address-level2" | "address-level3" | "address-level4" | "address-line1" | "address-line2" | "address-line3" | "bday" | "bday-day" | "bday-month" | "bday-year" | "cc-additional-name" | "cc-csc" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-family-name" | "cc-given-name" | "cc-name" | "cc-number" | "cc-type" | "country" | "country-name" | "current-password" | "email" | "family-name" | "given-name" | "honorific-prefix" | "honorific-suffix" | "impp" | "language" | "name" | "new-password" | "nickname" | "off" | "on" | "one-time-code" | "organization" | "organization-title" | "photo" | "postal-code" | "sex" | "street-address" | "tel" | "tel-area-code" | "tel-country-code" | "tel-extension" | "tel-local" | "tel-national" | "transaction-amount" | "transaction-currency" | "url" | "username",'off',false,false
ion-input,prop,autocorrect,"off" | "on",'off',false,false
ion-input,prop,autocorrect,boolean,false,false,false
ion-input,prop,autofocus,boolean,false,false,false
ion-input,prop,clearInput,boolean,false,false,false
ion-input,prop,clearInputIcon,string | undefined,undefined,false,false
Expand Down Expand Up @@ -1542,7 +1542,7 @@ ion-searchbar,scoped
ion-searchbar,prop,animated,boolean,false,false,false
ion-searchbar,prop,autocapitalize,string,'off',false,false
ion-searchbar,prop,autocomplete,"additional-name" | "address-level1" | "address-level2" | "address-level3" | "address-level4" | "address-line1" | "address-line2" | "address-line3" | "bday" | "bday-day" | "bday-month" | "bday-year" | "cc-additional-name" | "cc-csc" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-family-name" | "cc-given-name" | "cc-name" | "cc-number" | "cc-type" | "country" | "country-name" | "current-password" | "email" | "family-name" | "given-name" | "honorific-prefix" | "honorific-suffix" | "impp" | "language" | "name" | "new-password" | "nickname" | "off" | "on" | "one-time-code" | "organization" | "organization-title" | "photo" | "postal-code" | "sex" | "street-address" | "tel" | "tel-area-code" | "tel-country-code" | "tel-extension" | "tel-local" | "tel-national" | "transaction-amount" | "transaction-currency" | "url" | "username",'off',false,false
ion-searchbar,prop,autocorrect,"off" | "on",'off',false,false
ion-searchbar,prop,autocorrect,boolean,false,false,false
ion-searchbar,prop,cancelButtonIcon,string,config.get('backButtonIcon', arrowBackSharp) as string,false,false
ion-searchbar,prop,cancelButtonText,string,'Cancel',false,false
ion-searchbar,prop,clearIcon,string | undefined,undefined,false,false
Expand Down Expand Up @@ -1779,7 +1779,7 @@ ion-spinner,css-prop,--color
ion-split-pane,shadow
ion-split-pane,prop,contentId,string | undefined,undefined,false,true
ion-split-pane,prop,disabled,boolean,false,false,false
ion-split-pane,prop,when,boolean | string,QUERY['lg'],false,false
ion-split-pane,prop,when,boolean | string,'(min-width: 992px)',false,false
ion-split-pane,event,ionSplitPaneVisible,{ visible: boolean; },true
ion-split-pane,css-prop,--border,ios
ion-split-pane,css-prop,--border,md
Expand Down
78 changes: 40 additions & 38 deletions core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"loader/"
],
"dependencies": {
"@stencil/core": "4.43.0",
"@stencil/core": "^4.43.5",
"ionicons": "^8.0.13",
"tslib": "^2.1.0"
},
Expand All @@ -81,7 +81,7 @@
"@playwright/test": "^1.59.1",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.10.0",
"@stencil/angular-output-target": "^1.3.1",
"@stencil/react-output-target": "^1.5.2",
"@stencil/sass": "^3.0.9",
"@stencil/vue-output-target": "0.13.1",
Expand Down
Loading
Loading