Angular 21は2025年11月19日に公開されたメジャーバージョンです。新規プロジェクトからzone.jsが外れ、テストランナーはVitestが既定になり、実験的なSignal Formsが入りました。2026年9月時点で21系の最新パッチは21.2.23(2026年9月9日公開)で、サポートはLTS期間に入っています。
この記事ではv21固有の変更点と、v20から上げるときに手を入れる箇所を扱います。最新版のv22系についてはAngular 22.1とは?v22系の変更点とリリース体制の転換を実装目線で解説で整理しています。
まとめ:Angular 21で押さえる変更点と移行の要点
- サポート期限:Active期間は2026年6月3日に終了し、LTSは2027年6月まで。重大な修正とセキュリティパッチだけが出ます。
- ゾーンレス既定:
ng newで作るアプリはzone.jsなし。既存アプリはng updateでprovideZoneChangeDetection()が補われ、挙動は変わりません。 - 動作要件:Node.jsの公式対応範囲は^20.19.0 / ^22.12.0 / ^24.0.0、TypeScriptは5.9以上(21.2系は6.0まで)。5.9未満は非対応になりました。
- テスト:新規プロジェクトの
ng testはVitestで動きます。Karmaも選べ、Jasmineからの書き換えスキマティックがあります。 - Signal Forms:実験的APIです。21.0.9でテンプレートの
[field]が[formField]に改名されており、21.0.0時点のサンプルはそのままでは動きません。 - そのほか:HttpClientがルートで既定提供され、Angular Aria(developer preview)が加わりました。CLIのMCPサーバー(20.1.0で導入)には
ai_tutorが追加され、ゾーンレス移行ツールなどが安定版になっています。
Angular 21のリリース日とサポート期限
21.0.0の公開は2025年11月19日です。その後、21.1.0(2026年1月14日)と21.2.0(2026年2月25日)の2回のマイナーリリースが出ています。angular.devの公式サポート表(Versioning and releases)では、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 |
v21までのAngularは半年ごとにメジャーを出していました。v22から年1回のメジャーと、Active12か月・LTS12か月の体制に変わっています。v21のLTS終了は2027年6月で、v23の公開予定と同じ月です。v22を飛ばしてv23を待つ計画は、公開直後に2世代分の移行を終えなければならないため、保守案件ではv22への更新予算を2027年前半までに確保しておくのが現実的です。
動作要件:Node.js・TypeScript・RxJSの対応範囲
npmレジストリに登録された各パッケージのenginesとpeerDependenciesから、21.0.0と21.2.23の要件を並べます。
| 項目 | 21.0.0 | 21.2.23 |
|---|---|---|
| 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のCHANGELOGに記載され、21.2.23の@angular/compiler-cliも6.1未満までを許容します。一方、angular.devの互換表は21.2系も6.0未満と記載したままなので、TypeScript 6を使うなら21.2系の最新パッチでビルドとテストを通してから採用します。21.0系や21.1系のままTypeScript 6を入れるとピア依存の範囲外になるため、先に21.2系へ上げます。Node.js 20系は要件上は動きますが、Node.js側のサポートが2026年4月30日に終了しているので、新しく環境を作るなら22系か24系を選びます。
ゾーンレス既定化とprovideZoneChangeDetectionの意味
新規プロジェクトの初期構成
@angular/cli 21.2.24のng newで生成すると、package.jsonにzone.jsは入らず、app.config.tsにも変更検知のプロバイダーは書かれません。v21ではプロバイダーを書かない状態がゾーンレスです。
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideRouter(routes)
]
};
v20までは同じ目的でprovideZonelessChangeDetection()を明示していました。v21でも書いて構いませんが、必須ではありません。
既存アプリをng updateしたときに入るprovideZoneChangeDetection
21.0.0の破壊的変更として、CHANGELOGには「ZoneJSベースの変更検知のスケジューラーを既定では提供しなくなった」と書かれています。zone.jsで動いてきたアプリがそのまま上がると変更検知が変わってしまうため、@angular/coreのマイグレーションbootstrap-options-migrationがng update時に次の処理をします。
bootstrapApplicationやbootstrapModuleに変更検知のプロバイダーが無ければ、provideZoneChangeDetection()を追加する- 起動オプションの
ngZoneEventCoalescing・ngZoneRunCoalescingを、provideZoneChangeDetection({ eventCoalescing: true })のような引数へ移す - すでに
provideZoneChangeDetectionかprovideZonelessChangeDetectionがある場合は何もしない
v18〜v20のCLIで作ったスタンドアロン構成のアプリは、ゾーンレスを選んでいなければテンプレートの時点でapp.config.tsに次の1行を持っています。このため差分が出ず、zone.jsのまま動き続けます。v17のテンプレートにはこの行が無いので、v17で作ったアプリには引数なしのprovideZoneChangeDetection()が追加されます。
provideZoneChangeDetection({ eventCoalescing: true }),
つまり「v21に上げたらゾーンレスになる」わけではありません。ゾーンレスへの切り替えは、上げたあとに別作業として行います。なおignoreChangesOutsideZoneオプションは21.0.0で削除されました。起動オプションに書いていた場合は、同じマイグレーションが取り除きます。
zone.jsを外す手順と外せないコード
angular.devのゾーンレスガイドが示す手順は次の3つです。
provideZoneChangeDetectionを削除する(またはprovideZonelessChangeDetection()に置き換える)angular.jsonのbuildとtestの両ターゲットで、polyfillsからzone.jsとzone.js/testingを消すnpm uninstall zone.jsでパッケージを外す
外す前に確認するのは、状態変化をAngularに伝えていないコードです。テンプレートで読むシグナル、markForCheck()、AsyncPipe、テンプレートのイベントリスナーのいずれかで更新を通知していれば動きます。setTimeoutの中でプロパティを書き換えるだけのコンポーネントは画面が更新されません。
NgZone.onMicrotaskEmpty・onStable・onUnstableとNgZone.isStableに依存するコードは、ゾーンレスでは使えません。描画後の処理ならafterNextRenderかafterEveryRenderへ移します。公式ガイドは、コンポーネントが正しい通知の仕組みを使っているかを確かめる方法の1つとしてChangeDetectionStrategy.OnPushを挙げています。先にOnPush化を進めておくと、ゾーンレスへ切り替えたときの影響範囲を絞れます。CLIのMCPサーバーにあるonpush_zoneless_migrationツールは、この順序で移行計画を立てるためのものです。
Signal Forms(実験的API)の書き方と21.x中の改名
Signal Formsは@angular/forms/signalsから読み込む新しいフォームAPIで、v21では実験的(experimental)の扱いです。モデルをシグナルで持ち、form()にスキーマ関数を渡して検証ルールを宣言します。次のコードは21.2.23で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()・touched()・errors()もシグナルです。ControlValueAccessorを実装しなくても入力要素とモデルが双方向で同期します。シグナルの依存追跡そのものはuntrackedとcomputedシグナルの違い|Angular・SolidJS・Preactで依存追跡を外す使い方で詳しく扱っています。
実験的APIなので、21系のパッチ間でも名前が変わっています。
- 21.0.7(2026年1月7日)で
[formField]ディレクティブが追加され、21.0.9(2026年1月14日)で[field]が[formField]へ改名された - 21.2.0でReactive Formsと併用するための
SignalFormControl、パースエラー、フォーム単位の送信オプションが追加された
2025年11月の公開直後に書かれた解説は[field]とFieldを使っているため、コピーすると現行の21.2系ではテンプレートが通りません。業務フォームに採用するなら、パッチ単位でバージョンを固定し、更新のたびにCHANGELOGのformsの項を確認する運用が前提になります。既存のReactive Formsを急いで置き換える理由はv21の時点ではありません。
テスト環境:Vitest既定化とKarmaからの移行
新規プロジェクトのangular.jsonでは、testターゲットのビルダーが@angular/build:unit-testになり、実行エンジンはVitestです。21.2.24で生成したpackage.jsonにはvitest(^4.0.8)とjsdomが入り、Karmaの依存は含まれません。上のSignal Formsコンポーネントを次のspecで検証すると、Vitest 4.1.11で通過しました。
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を注入していますが、テストにもapp.config.tsにもprovideHttpClient()を書いていません。v21からHttpClientはルートインジェクターで既定提供されるためです。インターセプターなど設定を変えるときだけprovideHttpClient()を書き、テストでモックする場合もprovideHttpClientTesting()だけで足ります。
既存のKarma・Jasmine構成は、ng generate @schematics/angular:refactor-jasmine-vitestでspecの書き換えを始められます。このスキマティックは実験的で、Vitestランナー自体の安定版という位置づけとは異なります。Jasmineのspy APIやマッチャーの変換は含まれますが、fakeAsyncとtickはzone.js/testingに依存するので、ゾーンレスのテストではawait fixture.whenStable()やVitestのフェイクタイマーへ手で書き直します。テストが多いプロジェクトでは、ゾーンレス化とVitest移行を同じPRで進めず、先にKarmaのままアプリ側をゾーンレスにするほうが原因を切り分けやすくなります。
そのほかの変更:Angular Aria・MCPサーバー・テンプレートとアニメーション
Angular Aria(developer preview)
@angular/ariaは、WAI-ARIAのパターンに沿ったキーボード操作・フォーカス管理・ARIA属性をディレクティブで提供するヘッドレスなUIライブラリです。見た目のスタイルは持たず、21.2.14のエントリーポイントはaccordion・combobox・grid・listbox・menu・tabs・toolbar・treeの8つです。Angular Materialのように完成したデザインが要るならMaterial、自社のデザインシステムにアクセシビリティの振る舞いだけを載せたいならAriaという分担になります。developer previewのため、APIは21系の中でも変わる前提で扱います。
Angular CLIのMCPサーバー:既定ツールと実験的ツールの区分
ng mcpで起動するMCPサーバーを、AIエディターやエージェントから呼び出せます。21.2.24のCLIでは、既定で有効なツールと実験的なツールが次のように分かれています。
| 区分 | ツール名 |
|---|---|
| 安定 | get_best_practices / search_documentation |
| 安定 | find_examples / list_projects |
| 安定 | onpush_zoneless_migration / ai_tutor |
| 実験的 | modernize / build / test / e2e |
| 実験的 | devserver.start / devserver.stop / devserver.wait_for_build |
ビルドやテストをエージェントに実行させるツールは実験的の側にあります。CIの代わりに使う設計は避け、ドキュメント検索とベストプラクティス取得を中心に使うのが21系での妥当な範囲です。
テンプレート・コンパイラとアニメーションの変更
- テンプレート内で正規表現リテラルが書けるようになり、
@deferのon viewportにIntersectionObserverのオプション(rootMarginなど)を渡せるようになった SimpleChangesが型引数を取れるようになり、ngOnChangesで入力名の打ち間違いを検出できる- ホストバインディングの型チェック(
typeCheckHostBindings)が既定で有効になり、これまで見逃されていた型エラーがビルドで出ることがある NgClass・NgStyleをネイティブの[class]・[style]バインディングへ移すマイグレーション(ngclass-to-class-migration・ngstyle-to-style-migration)と、CommonModuleを個別importへ分解するcommon-to-standalone-migrationが追加された@angular/animationsは20.2.0で非推奨になっており、v21ではanimate.enter・animate.leaveとCSSで書く方式が推奨。入れ子のアニメーションは21.2.0で対応した
v20からv21への更新手順と破壊的変更
アップデートは1メジャーずつ進めます。v17から21へ上げる場合はv18、v19、v20、v21の順に、v18からの場合はv19、v20、v21の順にng updateを実行し、各段階でビルドとテストを通してから次へ進みます。v20からの手順は次のとおりです。
- Node.jsを22.12.0以上の22系または24系にそろえる。Angular 20.0系・20.1系では先に20.2系以降へ更新してから、TypeScriptを5.9系にそろえる
ng update @angular/core@21 @angular/cli@21を実行し、自動マイグレーションを適用させる- 差分を確認する(変更検知プロバイダーの追加、
ApplicationConfigのimport元、SSRのmain.server.ts) ng buildで出る型エラー(ホストバインディングなど)と、下表の破壊的変更に当たる箇所を直すng testで、ルーターの遷移完了を待たずに検証しているテストが落ちていないか確認する
21.0.0のng updateで動く主な自動マイグレーションと、手で直す必要がある変更は次のとおりです。
| 変更内容 | 対応 |
|---|---|
| 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の自動検出を廃止 | 手動(.npmrcで指定) |
| LessのjavascriptEnabledオプション非対応 | 手動 |
ルーターの遷移は、21.0.0から完了までにマイクロタスクを数回余分に消費するようになりました。遷移の直後に同期的にURLやDOMを検証しているテストは、await fixture.whenStable()などで遷移の完了を待つ形へ直します。
よくある質問
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まで対応します。
Angular 21でもNgIfやNgFor、CommonModuleは使えますか?
使えます。ただしNgIf・NgFor・NgSwitchは20.0で非推奨になっており、将来のメジャーで削除予定です。@if・@for・@switchへの置き換えはcontrol-flow-migrationで行えます。スタンドアロンコンポーネントでCommonModuleが自動で読み込まれることはなく、importsへの明示が必要な点はAngular 14のスタンドアロンコンポーネント|NgModule不要の書き方と移行手順で解説しています。
Angular 21でzone.jsを使い続けることはできますか?
できます。app.config.tsなどにprovideZoneChangeDetection()を書き、polyfillsにzone.jsを残せば従来どおり動きます。v20からng updateで上げた場合は、この構成が保たれます。
Angular 21で@angular/animationsはまだ使えますか?
パッケージは提供されていますが、20.2.0から非推奨です。npmレジストリ上でもanimate.enterとanimate.leaveを使うよう案内されているため、新しく書くアニメーションはこちらの方式にします。