Angular 14のスタンドアロンコンポーネント|NgModule不要の書き方と移行手順
Angular 14は2022年6月2日にリリースされ、NgModuleに属さずに動作する「スタンドアロンコンポーネント」をdeveloper previewとして導入しました。この仕組みはその後のバージョンで既定の書き方になり、2026年8月時点の最新版であるv22では、そもそもstandaloneを書く必要すらありません。本記事ではスタンドアロンコンポーネントに絞り、Angular 14時点の仕様と現在の書き方の差、公式ドキュメントに沿った移行手順を整理します(v14では型付きリアクティブフォームなども導入されましたが、ここでは扱いません)。バージョンごとの新機能そのものを追いたい場合はAngular v21 の概要と主な変更点 — 開発者体験の革新ポイントを参照してください。
まとめ
Angular 14のスタンドアロンコンポーネントは、@Componentデコレータのimportsに依存を直接書くことで、NgModuleの宣言なしにコンポーネントを動かす仕組みです。v14ではdeveloper preview、v15(2022年11月16日)で安定版APIに昇格し、v17でCLIの既定に、v19でstandalone: trueそのものが既定値になりました。
実装で最もつまずくのは、CommonModuleの扱いです。スタンドアロンコンポーネントでCommonModuleが自動的に組み込まれることはありません。*ngIfや*ngForを使うならimportsへの明示が必須で、書き忘れるとビルドは通ったまま実行時に描画されません。v18以降は@ifや@forへ置き換えればimport自体が不要になります。
そしてAngular 14は、公式のサポート対象から外れています。angular.devのリリースページは「Angular versions v2 to v19 are no longer supported.」と明記しており、v14で新規開発を始める判断は取るべきではありません。以下で、書き方の具体とv22までの移行経路を順に説明します。
Angular 14で入ったスタンドアロンコンポーネントの仕組み
NgModuleに代わって@Componentのimportsが依存を宣言する構造
従来のAngularでは、コンポーネントを動かすために必ずNgModuleのdeclarationsへ登録する必要がありました。スタンドアロンコンポーネントはこの登録を不要にし、テンプレートが必要とする依存をコンポーネント自身のimportsに書きます。依存の宣言場所がモジュールからコンポーネントへ移った、というのが変更の本質です。
Angular 14時点での最小のコンポーネントは次のようになります。
import { Component } from '@angular/core';
@Component({
standalone: true,
selector: 'app-hello',
template: 'Hello',
})
export class HelloComponent {}
公式APIリファレンスはimportsを「the standalone component’s template dependencies」と定義しています。ここに書くべきなのは、そのテンプレートが実際に使うディレクティブ・コンポーネント・パイプだけです。使わない依存を並べると、拡張診断NG8113「Unused standalone imports」が検出対象にします。公式はその弊害を「The unused imports add unnecessary noise to your code and can increase your compilation time.」と説明しており、コードのノイズとコンパイル時間の問題として扱われます。
developer previewだったv14時点の制約と生成コマンド
Angular 14公式ブログは、standaloneがdeveloper previewであり、安定APIではないため通常の後方互換ポリシーの外で変更されうると明言していました。互換性保証の対象外だったということです。当時のコンポーネント生成はng generate componentに--standaloneを付ける必要がありました。
安定版APIになったのはv15(2022年11月16日)です。公式ブログは、これらのAPIがdeveloper previewを卒業し安定APIの一部になったと発表しています。v14のコードをそのまま参考にすると、preview時点の制約や書き方を引きずることになります。実務でAngular 14のサンプルを読むときは、v15以降で何が変わったかを必ず確認してください。
CommonModuleは自動では入らない:ngIf・ngForを動かす3つの書き方
スタンドアロンコンポーネントを解説した日本語記事には「CommonModuleが自動で実装されるため手動インポートが不要」という説明が散見されますが、これは誤りです。angular.devのNgIfAPIリファレンスは「Exported by CommonModule」と記載しており、NgIfはCommonModule経由で提供されるディレクティブのままです。
import漏れはビルドを止めません。Angularは拡張診断NG8103「Missing control flow directive」でこの状況を検出しますが、公式が説明する結果は次のとおりです。
「Using a control flow directive without importing it will fail at runtime, as Angular attempts to bind to an ngIf property of the HTML element, which does not exist.」——存在しないngIfプロパティにバインドしようとして実行時に失敗する、つまり例外ではなく「条件分岐が効かず要素が出ない」という形で現れます。よくある「なぜか表示されない」の正体はこれです。
解決策は3つあり、どれを選ぶかで依存の粒度と将来の書き換えコストが変わります。
CommonModuleをまとめてimportする最短の書き方
Angular 14から使える、最も手数の少ない方法です。CommonModule一式を取り込むため、*ngIfも*ngForもdateパイプも追加設定なしで動きます。
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
standalone: true,
selector: 'app-list',
imports: [CommonModule],
template: '<p *ngIf="visible">表示</p>',
})
export class ListComponent {
visible = true;
}
手軽な反面、1つのディレクティブしか使っていなくてもCommonModule全体を依存として宣言することになります。既存コードの動作を止めずに移行したい段階では有効ですが、最終形として選ぶ書き方ではありません。
個別ディレクティブだけをimportして依存を絞る書き方
Angularは、CommonModuleのimportを「テンプレートが実際に必要とする最小限のディレクティブとパイプ」へ置き換える移行スキマティクスを公式に用意しています。
ng generate @angular/core:common-to-standalone
このスキマティクスは、*ngIfだけを使っているコンポーネントであれば、CommonModuleのimportをNgIfの指定へ書き換えます。asyncパイプを使っていればAsyncPipeが、jsonパイプを使っていればJsonPipeが併せて追加されます。手書きなら次の形です。
import { Component } from '@angular/core';
import { NgIf } from '@angular/common';
@Component({
standalone: true,
selector: 'app-list',
imports: [NgIf],
template: '<p *ngIf="visible">表示</p>',
})
export class ListComponent {
visible = true;
}
Angularが公式スキマティクスを用意して個別importへ寄せている事実は、CommonModuleの一括importが推奨形ではないことを示しています。ただしNgIf自体がv20.0で非推奨になっている点には注意してください。既存プロジェクトの整理としては有効でも、行き先はこの次の書き方です。
@if・@forへ置き換えてimport自体を無くす書き方
v17(2023年11月8日)で導入され、v18(2024年5月22日)で安定版になった組み込み制御フローを使えば、CommonModuleも個別ディレクティブも不要になります。公式の移行ガイドは「The new syntax is baked into the template, so you don’t need to import CommonModule anymore.」と説明しています。テンプレート構文そのものに組み込まれたため、依存として宣言する対象ではなくなった、という理屈です。
import { Component } from '@angular/core';
@Component({
standalone: true,
selector: 'app-list',
template: '@if (visible) { <p>表示</p> }',
})
export class ListComponent {
visible = true;
}
置き換えはng generate @angular/core:control-flowで自動化できます。NgIfのAPIリファレンスには「deprecated since v20.0」の表示とともに「Use the @if block instead. Intent to remove in a future major release」という注記が付いており、将来のメジャーリリースでの削除が予告されています。v18以上を使っているなら、新規コードは@ifと@forで書くべきです。*ngIfのまま残す理由は、v17以下に留まっている場合以外にありません。v17は構文自体は使えたものの安定版ではなく、互換性保証の対象外でした。
providersの置き場所で変わるサービスのDIスコープ
スタンドアロン化で最も挙動が変わるのが、サービスのインスタンス数です。NgModule時代はprovidersをモジュールに置き、そのモジュール配下のコンポーネントが同じインスタンスを共有していました。コンポーネントのprovidersに書いた場合、そのコンポーネントごとにインスタンスが作られ、コンポーネントが破棄されると一緒に破棄されます。
状態を持つサービスをコンポーネントのprovidersに書くと、画面遷移のたびに状態が初期化されます。アプリ全体で1つのインスタンスを共有したいなら、@Injectable({ providedIn: 'root' })を使うか、ブートストラップ時のprovidersに登録してください。「スタンドアロンだからコンポーネントに書く」と機械的に判断するのが典型的な失敗パターンです。
一方で、コンポーネントの責務分割やロジックをサービスへ切り出す判断は、NgModuleの有無とは無関係でした。スタンドアロンコンポーネントが変えたのは依存の宣言方法だけで、アーキテクチャの良し悪しを肩代わりするものではありません。責務分離の考え方そのものを整理したい場合はMVCとは?MVVM・MVPとの違いと使い分けを整理【2026年最新】が参考になります。
provideRouterとloadComponentで書くルーティングと遅延読み込み
スタンドアロン構成では、ルーターもNgModuleではなく関数で登録します。v15公式ブログによればprovideRouterはツリーシェイカブルで、未使用のルーター機能をバンドルから取り除いた結果、アプリのバンドル内のルーターコードのサイズが11%削減されたと報告されています。ルート単位の遅延読み込みはloadComponentで書きます。
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { HomeComponent } from './home.component';
const routes: Routes = [
{ path: 'home', component: HomeComponent },
{
path: 'admin',
loadComponent: () =>
import('./admin.component').then((m) => m.AdminComponent),
},
];
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes)],
});
NgModule時代のloadChildrenがモジュール単位だったのに対し、loadComponentはコンポーネント1つを遅延読み込みの単位にできます。分割の粒度を細かく取れる点が実務上の差です。HTTP通信を併用する場合はprovidersにprovideHttpClient()を追加してください。公式はHttpClientModuleを「older API」と位置づけ、複数のインジェクタに存在するとインターセプタの挙動が定まらないためprovideHttpClientを推奨すると注意喚起しています。
NgModuleプロジェクトを移行するスキマティクスの3段階
既存プロジェクトの移行は、手作業ではなく公式スキマティクスで進めます。コマンドは1つですが、同じコマンドを3回実行し、プロンプトで異なる段階を選ぶ設計です。
ng generate @angular/core:standalone
選択する段階は、(1) コンポーネント・ディレクティブ・パイプをスタンドアロンへ変換、(2) 不要になったNgModuleクラスを削除、(3) standalone APIでのブートストラップへ切り替え、という順序です。公式ドキュメントは、各段階のあいだにビルドと動作確認を挟むよう求めています。まとめて流して壊れた場合、どの段階が原因か切り分けられなくなるためです。
移行後は、周辺のスキマティクスで細部を整えます。CommonModuleの整理にはcommon-to-standalone、テンプレート構文の刷新にはcontrol-flow、不要になったimportの掃除にはcleanup-unused-importsを使います。いずれもng generate @angular/core:に続けて指定するかたちです。
Angular 14からv22までの標準化の流れとバージョン選択
スタンドアロンは1度の導入で完成したのではなく、複数のメジャーリリースをかけて既定へ移行しました。関係するバージョンを整理します。
| 版 | リリース日 | スタンドアロン関連の節目 | サポート状況 |
|---|---|---|---|
| v14 | 2022-06-02 | developer previewとして導入 | 終了 |
| v15 | 2022-11-16 | 安定版APIへ昇格 | 終了 |
| v17 | 2023-11-08 | ng generateの既定・@ifを導入 | 終了 |
| v18 | 2024-05-22 | @if・@forが安定版へ | 終了 |
| v19 | 2024-11-19 | standalone: trueが既定値 | 終了 |
| v20 | 2025-05-28 | NgIfなどを非推奨化 | LTS |
| v21 | 2025-11-19 | 既定 | LTS |
| v22 | 2026-06-03 | 既定 | Active |
v19以降で書くコードにはstandalone: trueを書きません。既定値になったためで、冒頭のコンポーネントは次の形に縮みます。
import { Component } from '@angular/core';
@Component({
selector: 'app-hello',
template: 'Hello',
})
export class HelloComponent {}
逆に、NgModuleに登録したいコンポーネントにはstandalone: falseを明示します。公式APIリファレンスも「Set standalone to false if you want to import the directive into an NgModule.」と記しています。v19へのng updateには、既存のstandalone: trueを削除し、NgModule所属のコンポーネントへstandalone: falseを付与する自動移行が同梱されているため、手作業での一括置換は不要です。
バージョン選択の結論は明確です。angular.devは「Angular versions v2 to v19 are no longer supported.」と記載しており、2026年8月時点でセキュリティ修正を受け取れるのはv20・v21・v22の3系統だけになりました。Angular 14のまま新規開発を始める、あるいは現行のv14プロジェクトを据え置く判断は、サポート切れを受け入れることと同義です。フレームワークの選定段階から見直す場合はWebアプリケーションフレームワークとは?主要フレームワーク比較と選び方【2026年版】もあわせて確認してください。
よくある質問
Angular 14は今も使えますか?
動作はしますが、公式サポートは終了しています。angular.devのリリースページには「Angular versions v2 to v19 are no longer supported.」と明記されており、2026年8月時点でサポート対象はv22(Active、2026年6月3日リリース)とv21・v20(LTS)の3つだけです。セキュリティ修正が提供されないため、稼働中のv14プロジェクトはアップグレード計画を立てるべき状態にあります。
スタンドアロンコンポーネントでngIfが動かないのはなぜですか?
importsにCommonModuleまたはNgIfを書いていないためです。スタンドアロンコンポーネントでCommonModuleが自動的に組み込まれる仕様はありません。NgIfのAPIリファレンスにも「Exported by CommonModule」と記載されています。Angularは拡張診断NG8103「Missing control flow directive」でこの状況を検出しますが、ビルドは通り、実行時に存在しないngIfプロパティへのバインドとして失敗します。v18以上なら、importの要らない@ifブロックへ置き換えるのが確実です。
standalone: true はもう書かなくてよいのですか?
v19(2024年11月19日リリース)以降は既定値になったため不要です。書いてあっても動作しますが、v19へのng updateに含まれる自動移行が既存のstandalone: trueを削除します。逆にNgModuleへ登録するコンポーネントにはstandalone: falseの明示が必要になり、この付与も同じ自動移行が行います。
NgModuleは廃止されるのですか?
2026年8月時点で廃止はされていません。standalone: falseを指定すればNgModuleベースの構成を維持できます。ただしCLIの生成物はv17以降すべてスタンドアロンになり、v19では既定値も反転したため、Angularが標準として想定しているのはスタンドアロン構成です。新規実装をNgModuleで書く理由は、既存コードとの整合以外にありません。
Angular 14のプロジェクトを一気に最新版へ上げられますか?
メジャーバージョンは1つずつ上げるのが公式の前提です。加えてスタンドアロン化はng generate @angular/core:standaloneを3段階に分けて実行する設計になっており、各段階でビルドと動作確認を挟むよう公式ドキュメントが求めています。v14からv22までは8世代あるため、バージョン更新とスタンドアロン移行を同時に進めず、工程を分けてください。