---
title: "Angular 21の変更点とLTS期限｜provideZoneChangeDetectionの意味とv20からの更新手順【21.2.26対応】"
url: "https://www.issoh.co.jp/tech/details/10025/"
published: 2025-11-28
updated: 2026-11-09
categories: ["フレームワーク"]
publisher: "株式会社一創"
---

# Angular 21の変更点とLTS期限｜provideZoneChangeDetectionの意味とv20からの更新手順【21.2.26対応】

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系の変更点とリリース体制の転換を実装目線で解説](/tech/details/15980/)

## まとめ：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回です。[公式のサポート表](https://angular.dev/reference/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 |

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](https://github.com/angular/angular/blob/21.2.x/CHANGELOG.md)から、直近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](https://github.com/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未満までを許容します。ただし[公式の互換表](https://angular.dev/reference/versions)は21.2系も6.0未満のままなので、[TypeScript 6.0](/tech/details/11066/)を入れるなら21.2系の最新パッチでビルドとテストを通してから採用します。Node.js 20系はサポートが2026年4月30日に終わっているため、新しく環境を作るなら22系か24系を選びます（[Node.jsのLTSの現状](/tech/details/17906/)）。2026年10月にLTSとなった26系は、`engines`の範囲には入りますが互換表に載っていないので、v21では24系までにとどめます。

## ゾーンレス既定化とprovideZoneChangeDetectionの意味

### provideZoneChangeDetectionが指定するもの

`provideZoneChangeDetection()`は、アプリの変更検知を`NgZone`（zone.js）ベースにするプロバイダーです。v21では何も書かない状態がゾーンレスなので、zone.jsで動かし続けたいアプリだけがこれを書きます。[公式API](https://angular.dev/api/core/provideZoneChangeDetection)が受け付けるオプションは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を外す手順と画面が更新されなくなるコード

[公式のゾーンレスガイド](https://angular.dev/guide/zoneless)の手順は次の3つです。

1. `provideZoneChangeDetection`を削除する（`main.ts`のスプレッド式も含む）
2. `angular.json`の`build`と`test`の両ターゲットで、`polyfills`から`zone.js`と`zone.js/testing`を消す
3. `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](https://github.com/angular/angular/blob/22.2.x/CHANGELOG.md)には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](/tech/details/16023/)です。上の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: 'yamada@example.com' });
    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](/tech/details/6970/)のパターンに沿ったキーボード操作・フォーカス管理・ARIA属性をディレクティブで提供するヘッドレスUIです（developer preview）。自社のデザインシステムにアクセシビリティの振る舞いだけを載せたいならAria、完成したデザインが要るならAngular Materialという分担になります。

`ng mcp`で起動するCLIのMCPサーバーは、[v21のドキュメント](https://v21.angular.dev/ai/mcp)で既定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からの手順は次のとおりです。

1. Node.jsを22.12.0以上の22系または24系にそろえる。TypeScriptはv21への更新と同時に5.9系へそろえる
2. `ng update @angular/core@21 @angular/cli@21`を実行し、自動マイグレーションを適用させる
3. 差分を確認し（変更検知プロバイダーの追加、制御フローの書き換え、SSRの`main.server.ts`）、`ng build`で出る型エラーを直す
4. `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の破壊的変更](https://github.com/angular/angular/blob/22.2.x/CHANGELOG.md)のうち次の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の解説](/tech/details/15980/)で扱っています。

## よくある質問

### 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系の最新パッチへ上がります。

## 関連記事

- [Angular 22.1とは？v22系の変更点とリリース体制の転換を実装目線で解説](/tech/details/15980/)
- [i18n（国際化）とは？読み方・l10nとの違いとAngularでの実装手順【Angular 22】](/tech/details/8884/)
- [Node.js LTSの最新は24、10月28日に26へ昇格｜v22・v24のEOLと確認・固定・移行の手順](/tech/details/17906/)
- [TypeScript 6.0とは｜5から6への移行手順とts5to6・7.0との違い【6.0.3対応】](/tech/details/11066/)
- [Vitestとは？Viteネイティブなテスト基盤の仕組みと採用判断を実装目線で解説](/tech/details/16023/)

---

出典: [Angular 21の変更点とLTS期限｜provideZoneChangeDetectionの意味とv20からの更新手順【21.2.26対応】](<https://www.issoh.co.jp/tech/details/10025/>)（株式会社一創）
