Angular 21は2025年11月19日に公開されたメジャーバージョンです。新規プロジェクトからzone.jsが外れ、テストランナーはVitestが既定になり、実験的なSignal Formsが入りました。2026年11月時点の21系の最新パッチは@angular/core 21.2.26(2026年11月2日公開)で、サポートはLTS期間に入っています。
この記事はv21固有の変更点と、v20から上げるときに手を入れる箇所を扱います。最新版はこちら:Angular 22.1とは?v22系の変更点とリリース体制の転換を実装目線で解説
まとめ:Angular 21のサポート期限・更新先の版・移行の要点
- サポート期限:v21のLTSは2027年6月まで。v20のLTSは2026年11月28日に終わるので、v20のまま残すプロジェクトは今月中に更新の判断が要ります。
- 入れるべき版:21.2.25より前の版には、SSRのDoSやオープンリダイレクトの修正が入っていません。21系を使うなら21.2.26まで上げます。
- ゾーンレス既定:
ng newで作るアプリはzone.jsなし。既存アプリはng updateでprovideZoneChangeDetection()が補われ、zone.jsのまま動き続けます。 - 動作要件:Node.jsは^20.19.0 / ^22.12.0 / ^24.0.0、TypeScriptは5.9以上。5.9未満は非対応です。
- テスト:新規プロジェクトの
ng testはVitestで動きます。既存のKarma構成はng updateでは置き換わりません。 - Signal Forms:v21では実験的APIで、21.0.9で
[field]が[formField]に改名されました。public APIになったのはv22.0です。
Angular 21のリリース日とサポート期限
21.0.0の公開は2025年11月19日、マイナーリリースは21.1.0(2026年1月14日)と21.2.0(2026年2月25日)の2回です。公式のサポート表では、v21は次の位置にあります。
| バージョン | 状態 | 公開日 | Active終了 | LTS終了 |
|---|---|---|---|---|
| v22 | Active | 2026-06-03 | 2027-06 | 2028-06 |
| v21 | LTS | 2025-11-19 | 2026-06-03 | 2027-06 |
| v20 | LTS | 2025-05-28 | 2025-11-19 | 2026-11-28 |
LTS期間に出るのは重大な修正とセキュリティパッチだけです。v21のLTS終了はv23の公開予定と同じ2027年6月なので、v22を飛ばしてv23を待つと公開直後に2世代分の移行が要ります。保守案件なら2027年前半にv22への更新予算を置きます。
21.2系の最新パッチと直近の修正内容
@angular/core系とCLIはパッチ番号が別々に進みます。2026年11月9日時点のnpmのv21-ltsタグは、coreが21.2.26、CLIが21.2.29です。21.2.xのCHANGELOGから、直近4回の中身を抜き出します。
| 版 | 公開日 | 修正対象 | 影響する構成 |
|---|---|---|---|
| 21.2.26 | 2026-11-02 | ngUpgradeのダウングレード | AngularJS併用のみ |
| 21.2.25 | 2026-09-30 | SSRのURL解決・ルーター | SSR |
| 21.2.24 | 2026-09-23 | 数値URLキーのDoS | SSR |
| 21.2.23 | 2026-09-09 | 不正DOCTYPEの無限ループ | SSR |
v21に影響するセキュリティ勧告35件と更新の優先度
angular/angularのSecurity Advisoriesを2026年11月9日に集計すると、v21の正式版に修正が出た勧告は35件(high 27件、medium 8件)あります。このうち20件はLTSに入った2026年6月3日以降の公開です。「LTSだから変更が少なく、放置してよい」という前提は成り立ちません。
| パッケージ | 件数 | 主な内容 |
|---|---|---|
| @angular/platform-server | 11 | SSRのXSS・SSRF |
| @angular/common | 8 | HttpTransferCache・DoS |
| @angular/core | 8 | サニタイズ回避・XSS |
| @angular/compiler | 7 | テンプレートのXSS |
| @angular/router | 3 | SSRのDoS |
| @angular/service-worker | 3 | リダイレクト時の漏えい |
件数は1件が複数パッケージにまたがるときは両方に数えています。SSRを本番で動かしているアプリは、@angular/platform-serverと@angular/routerの修正が届く21.2.25以上が最低ラインです。クライアント描画だけのアプリでも、@angular/coreと@angular/compilerのサニタイズ修正は対象になります。21.2系の中の更新はAPIの破壊的変更を伴わない修正が中心なので、更新を見送る理由は基本的にありません。
更新はng update @angular/core@21 @angular/cli@21で21系の最新パッチへ上がります。v20系の修正は2026年11月28日のLTS終了で止まる予定です。先にLTSが切れたv19系は、2026年8月以降の勧告の修正版の欄に「End of Support / LTS expired; will not be patched」と書かれています。
動作要件:Node.js・TypeScript・RxJSの対応範囲
npmに登録されたenginesとpeerDependenciesから、21.0.0と21.2.26の要件を並べます。
| 項目 | 21.0.0 | 21.2.26 |
|---|---|---|
| Node.js | ^20.19.0 || ^22.12.0 || >=24.0.0 | 同左 |
| TypeScript | >=5.9 <6.0 | >=5.9 <6.1 |
| RxJS | ^6.5.3 || ^7.4.0 | 同左 |
| zone.js(使う場合) | ~0.15.0 | ~0.15.0 || ~0.16.0 |
TypeScript 6への対応は21.2.0で入り、21.2.26の@angular/compiler-cliは6.1未満までを許容します。ただし公式の互換表は21.2系も6.0未満のままなので、TypeScript 6.0を入れるなら21.2系の最新パッチでビルドとテストを通してから採用します。Node.js 20系はサポートが2026年4月30日に終わっているため、新しく環境を作るなら22系か24系を選びます(Node.jsのLTSの現状)。2026年10月にLTSとなった26系は、enginesの範囲には入りますが互換表に載っていないので、v21では24系までにとどめます。
ゾーンレス既定化とprovideZoneChangeDetectionの意味
provideZoneChangeDetectionが指定するもの
provideZoneChangeDetection()は、アプリの変更検知をNgZone(zone.js)ベースにするプロバイダーです。v21では何も書かない状態がゾーンレスなので、zone.jsで動かし続けたいアプリだけがこれを書きます。公式APIが受け付けるオプションは2つです。
eventCoalescing:バブリングで連続するイベントの変更検知を1回にまとめるrunCoalescing:同じイベントループ内のNgZone.run()の変更検知を1回にまとめる
NgModule構成では、このプロバイダーをbootstrapModule()のapplicationProvidersに渡します。
zone.jsを読み込まないままこのプロバイダーを書くと、起動時にNG0908: In this configuration Angular requires Zone.jsで失敗します。@angular/core 21.2.26でcreateApplication()に渡して確認した文言です。polyfillsからzone.jsを消す作業とプロバイダーの削除は、必ず同時に行います。
新規プロジェクトの初期構成
ng newで生成すると、package.jsonにzone.jsは入らず、app.config.tsにも変更検知のプロバイダーは書かれません。
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideRouter(routes)
]
};
v20までは同じ目的でprovideZonelessChangeDetection()を明示していました。v21でも書けますが必須ではありません。Node.js 24.22.2、Angular CLI 21.2.29、Angular core 21.2.26の環境で、既定テンプレートをng buildのproduction構成でビルドし、ng new --zoneless=falseで作った場合と比べると、初期バンドルはゾーンレスが214.34 kB(推定転送量58.64 kB)、zone.jsありが253.20 kB(同70.85 kB)で、差は38.86 kB(同12.21 kB)でした。
ng updateが足す場所:app.config.tsではなくmain.ts
21.0.0のCHANGELOGは「ZoneJSベースの変更検知のスケジューラーを既定では提供しなくなった」ことを破壊的変更に挙げています。アプリの挙動が変わらないよう、@angular/coreのbootstrap-options-migrationがng update時に動きます。v18〜v20のCLIで作ったスタンドアロン構成は、テンプレートの時点でapp.config.tsにprovideZoneChangeDetection({ eventCoalescing: true })を持っているので差分が出ません。
プロバイダーが無いアプリ(v17のテンプレートなど)で更新すると、次の差分が入ります。
+import { provideZoneChangeDetection } from "@angular/core";
-bootstrapApplication(App, appConfig)
+bootstrapApplication(App, {...appConfig, providers: [provideZoneChangeDetection(), ...appConfig.providers]})
書き換えられるのはapp.config.tsではなくsrc/main.tsです。app.config.tsだけを見て「provideZoneChangeDetectionが無いからゾーンレスになった」と判断すると誤ります。いずれの場合もzone.jsはpackage.jsonとpolyfillsに残ったままで、「v21に上げたらゾーンレスになる」わけではありません。NgModule構成ではmain.tsのbootstrapModuleが書き換えられ、既定テンプレートのngZoneEventCoalescing: trueは{ applicationProviders: [provideZoneChangeDetection({ eventCoalescing: true })] }へ移ります。ignoreChangesOutsideZoneオプションは21.0.0で削除され、起動オプションに書いていれば同じマイグレーションが取り除きます。
zone.jsを外す手順と画面が更新されなくなるコード
公式のゾーンレスガイドの手順は次の3つです。
provideZoneChangeDetectionを削除する(main.tsのスプレッド式も含む)angular.jsonのbuildとtestの両ターゲットで、polyfillsからzone.jsとzone.js/testingを消すnpm uninstall zone.jsでパッケージを外す
外す前に確かめるのは、状態変化をAngularに伝えていないコードです。テンプレートで読むシグナル、markForCheck()、AsyncPipe、テンプレートのイベントリスナーのいずれかで通知していれば動きます。setTimeoutの中でプロパティを書き換えるだけのコンポーネントは、21.2.26のテストでプロパティの値が1になってもDOMの表示は0のままでした。同じ処理をシグナルのset()にすると表示は1に更新されます。
NgZone.onMicrotaskEmpty・onStable・onUnstableはゾーンレスでは発火せず、NgZone.isStableは常にtrueを返します。描画後の処理ならafterNextRenderかafterEveryRenderへ移します。NgZone.runとNgZone.runOutsideAngularは残して構いません。先にChangeDetectionStrategy.OnPushへ移しておくと、切り替えたときの影響範囲を絞れます。CLIのMCPサーバーのonpush_zoneless_migrationは、この順序で移行計画を立てるツールです。
Signal Forms(実験的API)の書き方と21.x中の改名
Signal Formsは@angular/forms/signalsから読み込むフォームAPIで、v21では実験的(experimental)の扱いです。モデルをシグナルで持ち、form()にスキーマ関数を渡して検証ルールを宣言します。次のコードは@angular/core 21.2.26でng buildとng testを通しました。
import { Component, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { form, FormField, required, email } from '@angular/forms/signals';
interface Signup {
name: string;
mail: string;
}
@Component({
selector: 'app-signup',
imports: [FormField],
template: `
<label for="signup-name">氏名</label>
<input id="signup-name" [formField]="signupForm.name" />
<label for="signup-mail">メールアドレス</label>
<input id="signup-mail" [formField]="signupForm.mail" type="email" />
@if (signupForm.mail().invalid() && signupForm.mail().touched()) {
<p>メールアドレスの形式を確認してください</p>
}
<button [disabled]="signupForm().invalid()" (click)="send()">送信</button>
`,
})
export class SignupComponent {
private http = inject(HttpClient);
model = signal<Signup>({ name: '', mail: '' });
signupForm = form(this.model, (p) => {
required(p.name);
required(p.mail);
email(p.mail);
});
send() {
this.http.post('/api/signup', this.model()).subscribe();
}
}
各フィールドはsignupForm.mail()のように呼び出すと状態が取れ、invalid()やerrors()もシグナルです。
実験的APIなので、21系のパッチ間でも名前が変わっています。21.0.7(2026年1月7日)で[formField]ディレクティブが追加され、21.0.9(2026年1月14日)で[field]が[formField]へ改名されました。2025年11月の公開直後の解説は[field]とFieldを使っているので、コピーすると21.2系ではテンプレートが通りません。
v21のまま業務フォームに採用するなら、パッチ単位で版を固定し、更新のたびにCHANGELOGのformsの項を確認する運用が前提です。22.0.0のCHANGELOGにはSignal FormsのAPIをpublic APIへ昇格した変更があり、21.2系の@experimental 21.0.0は解除されていません。新規のフォームをSignal Formsで書きたいプロジェクトは、v22へ上げてから採用するほうが手戻りが小さくなります。既存のReactive Formsを置き換える理由は、v21の時点ではありません。
テスト環境:Vitest既定化とKarmaからの移行
新規プロジェクトのangular.jsonでは、testターゲットのビルダーが@angular/build:unit-testになり、実行エンジンはVitestです。上のSignal Formsコンポーネントは次のspecで検証しています。
import { TestBed } from '@angular/core/testing';
import { SignupComponent } from './signup';
describe('SignupComponent', () => {
it('必須と形式の検証が効く', async () => {
const fixture = TestBed.createComponent(SignupComponent);
await fixture.whenStable();
const c = fixture.componentInstance;
expect(c.signupForm().invalid()).toBe(true);
c.model.set({ name: '山田', mail: '[email protected]' });
expect(c.signupForm().valid()).toBe(true);
c.model.update((v) => ({ ...v, mail: 'not-mail' }));
expect(c.signupForm.mail().errors().map((e) => e.kind)).toEqual(['email']);
});
});
このコンポーネントはHttpClientを注入していますが、provideHttpClient()はどこにも書いていません。v21からHttpClientはルートインジェクターで既定提供されるためです。インターセプターなど設定を変えるときだけprovideHttpClient()を書きます。
既存のKarma・Jasmine構成は、実験的なスキマティックng generate @schematics/angular:refactor-jasmine-vitestでspecの書き換えを始められます。fakeAsyncとtickはzone.js/testingに依存するので、ゾーンレスのテストではawait fixture.whenStable()やVitestのフェイクタイマーへ手で書き直します。ゾーンレス化とVitest移行は同じPRで進めず、先にKarmaのままアプリ側をゾーンレスにすると、落ちたテストの原因を切り分けられます。
そのほかの変更:Angular Aria・MCPサーバー・テンプレート
@angular/ariaは、WAI-ARIAのパターンに沿ったキーボード操作・フォーカス管理・ARIA属性をディレクティブで提供するヘッドレスUIです(developer preview)。自社のデザインシステムにアクセシビリティの振る舞いだけを載せたいならAria、完成したデザインが要るならAngular Materialという分担になります。
ng mcpで起動するCLIのMCPサーバーは、v21のドキュメントで既定6種と実験的7種のツールに分かれています。buildやtestを実行するツールは実験的の側なので、CIの代わりに使う設計は避けます。
- テンプレートで正規表現リテラルが書け、
@deferのon viewportにrootMarginなどのオプションを渡せる SimpleChangesが型引数を取れるようになり、ngOnChangesで入力名の打ち間違いを検出できる- ホストバインディングの型チェック(
typeCheckHostBindings)が既定で有効になり、見逃されていた型エラーがビルドで出ることがある @angular/animationsは20.2.0で非推奨で、v21ではanimate.enter・animate.leaveとCSSで書く方式が推奨される
v20からv21への更新手順と破壊的変更
アップデートは1メジャーずつ進めます。v17から21へ上げる場合はv18、v19、v20、v21の順にng updateを実行し、各段階でビルドとテストを通します。v20からの手順は次のとおりです。
- Node.jsを22.12.0以上の22系または24系にそろえる。TypeScriptはv21への更新と同時に5.9系へそろえる
ng update @angular/core@21 @angular/cli@21を実行し、自動マイグレーションを適用させる- 差分を確認し(変更検知プロバイダーの追加、制御フローの書き換え、SSRの
main.server.ts)、ng buildで出る型エラーを直す ng testで、ルーターの遷移完了を待たずに検証しているテストが落ちていないか確認する
ng updateで自動的に書き換わるコード:*ngIfと*ngFor
21.0.0のマイグレーションにはcontrol-flow-migrationが含まれ、任意ではなく必ず実行されます。Node.js 24.22.2上で@angular/cli 20.3系のアプリを21.2系へ更新すると、*ngIfと*ngForのテンプレートは次のように変わりました。
<!-- 更新前 -->
<p *ngIf="show; else no">{{ title | uppercase }}</p>
<ng-template #no><p>none</p></ng-template>
<li *ngFor="let a of items; trackBy: tb">{{ a }}</li>
<!-- 更新後 -->
@if (show) {
<p>{{ title | uppercase }}</p>
} @else {
<p>none</p>
}
@for (a of items; track tb($index, a)) {
<li>{{ a }}</li>
}
trackByはtrack tb($index, a)へ移され、同じテンプレートの[ngClass]と[ngStyle]、コンポーネントのimports: [CommonModule]はそのまま残りました。差分が大きくなるので、更新用のブランチでは他の変更を混ぜないでください。ngClass・ngStyleのネイティブバインディング化とCommonModuleの分解は、ng generate @angular/core:ngclass-to-classなどを別途実行する任意の作業です。
既定テンプレートのまま作ったv20アプリを更新した場合、書き換わったのはpackage.jsonとロックファイルだけで、srcの差分はゼロでした。angular.jsonのtestビルダーも@angular/build:karmaのままで、ng updateではVitestへ移りません。
破壊的変更の自動移行と手動対応の区分
| 変更内容 | 対応 |
|---|---|
| zone.js用スケジューラーの既定提供を廃止 | 自動(bootstrap-options-migration) |
| ApplicationConfigのplatform-browserからの提供を削除 | 自動(application-config-core) |
| Router.lastSuccessfulNavigationがシグナル化 | 自動(呼び出し形式へ変換) |
| SSRのbootstrapにBootstrapContextが必要 | 自動(main.server.tsを更新) |
| TypeScript 5.9未満の非対応 | 手動 |
| ComponentのmoduleId・interpolation削除 | 手動 |
| NgModuleFactory・UpgradeAdapter削除 | 手動 |
| cnpmの自動検出を廃止 | npmへ切替・.npmrcにレジストリを設定 |
| LessのjavascriptEnabledオプション非対応 | 手動 |
ルーターの遷移は、21.0.0から完了までにマイクロタスクを数回余分に消費します。遷移の直後に同期的にURLやDOMを検証しているテストは、await fixture.whenStable()で完了を待つ形へ直します。
v21に留まるかv22へ上げるかの判断
v21のLTSは2027年6月まであるので、急いで上げる必要はありません。ただしv22の更新は21系の更新より重く、22.0.0の破壊的変更のうち次の3点は事前に調べておきます。
changeDetection未指定のコンポーネントがOnPush扱いになる(従来の挙動はChangeDetectionStrategy.Eagerで残す)- TypeScriptは6.0のみ(
>=6.0 <6.1)で、5.9は非対応 - Node.jsの要件が^22.22.3 / ^24.15.0 / ^26.0.0へ上がり、20系は使えない
21.2.26のアプリでng update @angular/core@22 @angular/cli@22を実行すると、TypeScriptは6.0.3へ上がり、changeDetectionの無いコンポーネントにはChangeDetectionStrategy.Eagerが自動で付きました。既存の画面は従来の更新方式のままで、OnPushになるのは新しく作るコンポーネントです。依存ライブラリがTypeScript 6に対応しているかどうかが、実質的な判断材料になります。Signal Formsを使う予定があるなら、v21に留まる理由は薄くなります。判断の軸はAngular 22.1の解説で扱っています。
よくある質問
Angular 21はいつまでサポートされますか?
angular.devのサポート表では、v21のActive期間は2026年6月3日に終了し、LTSは2027年6月までです。LTS期間に提供されるのは重大な修正とセキュリティパッチのみで、新機能は入りません。
Angular 21に必要なNode.jsとTypeScriptのバージョンは?
Node.jsは^20.19.0、^22.12.0、^24.0.0のいずれかです。TypeScriptは5.9以上で、21.0系と21.1系は6.0未満、21.2系は6.0まで対応します。Node.js 20系はサポートが終わっているので、22系か24系を選びます。
Angular 21でもNgIfやNgFor、CommonModuleは使えますか?
使えます。NgIf・NgFor・NgSwitchは20.0で非推奨になり、将来のメジャーで削除予定です。v20からng updateで上げると、テンプレートの*ngIfと*ngForは@ifと@forへ自動で書き換わります。スタンドアロンコンポーネントではCommonModuleが自動で読み込まれることはなく、importsへの明示が必要です。
provideZoneChangeDetectionは削除してよいですか?
zone.jsに依存するコードが残っているうちは削除しません。app.config.tsなどから消すと、そのアプリはゾーンレスで動きます。シグナル、markForCheck()、AsyncPipe、テンプレートのイベントで更新を通知できていない画面は描画されなくなるので、先にOnPush化で確認してから外します。
Angular 21.2.26には更新すべきですか?
21系を使っているなら更新します。21.2.26の修正自体はngUpgradeの1件だけですが、21.2.25より前の版にはSSRのDoSとオープンリダイレクトの修正が入っていません。ng update @angular/core@21 @angular/cli@21で21系の最新パッチへ上がります。