Angular Standalone Components: Migration und Best Practices für Angular 22
Vollständiger Leitfaden zur Migration von Angular-Anwendungen von NgModules zu Standalone-Komponenten. Behandelt die offizielle 3-stufige CLI-Migration, Selectorless Components, OnPush als Standard und Lazy Loading mit loadComponent in Angular 22.

Angular Standalone Components machen NgModules überflüssig, reduzieren Boilerplate-Code und ermöglichen feingranulares Lazy Loading in der gesamten Anwendung. Seitdem Angular 19 Standalone zum Standard erklärt hat und Angular 22 Selectorless Components als stabil eingeführt hat, ist die Migration alter modulbasierter Codebasen sowohl unkompliziert als auch wirkungsvoll geworden.
Das offizielle Angular-CLI-Schematic übernimmt den Großteil der Migration automatisch in drei Durchgängen. Eine typische Enterprise-Anwendung kann die Konvertierung in einem einzigen Sprint abschließen, wobei die Bundle-Größen dank komponentenbezogenem Lazy Loading um 30-50 % schrumpfen.
NgModules vs. Standalone Components: Was sich geändert hat
NgModules dienten seit Angular 2 als Kompilierungskontext für Komponenten. Jede Komponente, Direktive und Pipe musste in genau einem Modul deklariert werden, und gemeinsam genutzte Funktionalität erforderte sorgfältig orchestrierte Modul-Imports und -Exports. Dies führte zu enger Kopplung zwischen unabhängigen Features und erschwerte das Tree-Shaking.
Standalone Components kehren dieses Modell um. Jede Komponente deklariert ihre eigenen Abhängigkeiten direkt im imports-Array des @Component-Decorators. Keine Modulregistrierung, keine geteilten Module, keine Barrel-Exports der halben Anwendung. Ab Angular 22 wird dieses Muster durch Selectorless Components weiter vereinfacht, die direkte Imports ohne String-Selektoren ermöglichen.
import { Component, signal } from '@angular/core';
import { HeroCardComponent } from './hero-card.component';
import { SearchPipe } from '../pipes/search.pipe';
@Component({
selector: 'app-hero-list',
standalone: true,
imports: [HeroCardComponent, SearchPipe],
template: \`
<div class="hero-grid">
@for (hero of heroes() | search:query(); track hero.id) {
<app-hero-card [hero]="hero" />
}
</div>
\`
})
export class HeroListComponent {
heroes = signal<Hero[]>([]);
query = signal('');
}Das imports-Array ersetzt den gesamten NgModule-Abhängigkeitsgraphen. Der Bundler sieht genau, welche Komponenten, Pipes und Direktiven jede Datei benötigt, was präzises Tree-Shaking ermöglicht. Das CommonModule wird in Angular 22 nicht mehr benötigt, da die Control-Flow-Syntax (@for, @if) direkt in den Compiler integriert ist.
Der 3-stufige CLI-Migrationsprozess
Angular bietet ein automatisiertes Schematic, das die Migration in drei aufeinanderfolgenden Durchgängen abwickelt. Jeder Schritt baut auf dem vorherigen auf, und das Projekt sollte zwischen den Durchgängen sauber kompilieren.
Schritt 1: Deklarationen in Standalone umwandeln
Der erste Durchgang scannt jede Komponente, Direktive und Pipe im Projekt, fügt standalone: true hinzu und verschiebt die notwendigen Imports aus dem übergeordneten NgModule in das eigene imports-Array jeder Komponente.
# Step 1: Convert all declarations to standalone
ng generate @angular/core:standalone --path=src/appWählen Sie bei der Aufforderung "Convert all components, directives and pipes to standalone" aus. Das Schematic verwendet statische Analyse zur Auflösung von Abhängigkeiten, daher werden Komponenten mit Metadaten, die zur Build-Zeit nicht analysiert werden können, mit einer Warnung übersprungen.
Schritt 2: Überflüssige NgModules entfernen
Da nun alle Deklarationen Standalone sind, werden viele NgModules zu leeren Hüllen. Dieser Durchgang identifiziert Module, die nur Standalone-Deklarationen re-exportiert haben, und entfernt sie.
# Step 2: Remove empty NgModules
ng generate @angular/core:standalone --path=src/appWählen Sie "Remove unnecessary NgModule classes". Module, die noch Provider, Routenkonfigurationen enthalten oder von mehreren anderen Modulen importiert werden, bleiben mit einem TODO-Kommentar zur manuellen Überprüfung erhalten.
Angular 22 erfordert TypeScript 6 und Node.js 26. Ältere Versionen werden nicht unterstützt. Führen Sie ng version aus, um die Kompatibilität vor Beginn der Migration zu prüfen.
Schritt 3: Auf Standalone-Bootstrap umstellen
Der letzte Durchgang ersetzt das Root-NgModule durch Angulars bootstrapApplication-API und konvertiert die Root-Komponente in Standalone.
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch(err => console.error(err));import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './interceptors/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor]))
]
};Das ApplicationConfig-Muster ersetzt die providers- und imports-Arrays des Root-Moduls. Alle Provider-Funktionen (provideRouter, provideHttpClient, provideAnimations) funktionieren direkt ohne Modul-Wrapper.
Angular 22: OnPush als Standard und Selectorless Components
Angular 22 führte zwei Änderungen ein, die die Entwicklung mit Standalone Components betreffen: OnPush als standardmäßige Change-Detection-Strategie und Selectorless Components als stabil markiert.
OnPush als Standard
Neue Komponenten, die in Angular 22 generiert werden, verwenden standardmäßig ChangeDetectionStrategy.OnPush. Dies fördert signalbasierte Reaktivität gegenüber Zone.js-gesteuerten Prüfungen. Während der Migration fügt das Schematic den bestehenden Komponenten explizit changeDetection: ChangeDetectionStrategy.Default hinzu, um Rückwärtskompatibilität zu gewährleisten.
// Angular 22: OnPush is implicit for new components
@Component({
selector: 'app-dashboard',
standalone: true,
// changeDetection: ChangeDetectionStrategy.OnPush is now the default
template: \`<h1>{{ title() }}</h1>\`
})
export class DashboardComponent {
title = signal('Dashboard');
}Für migrierte Anwendungen empfiehlt sich die schrittweise Einführung von OnPush durch den Ersatz von Klasseneigenschaften durch Signals. Dies entspricht der Signal-First-Architektur, die Angular 22 fördert.
Selectorless Components (Stabil)
Selectorless Components ermöglichen den direkten Import von Komponenten in Templates ohne Definition von String-Selektoren. Dies eliminiert Namenskonflikte bei Selektoren in großen Codebasen und bietet bessere Typsicherheit.
// Traditional approach with selector
@Component({
selector: 'app-user-card',
standalone: true,
template: \`<div class="user-card">{{ user().name }}</div>\`
})
export class UserCardComponent {
user = input.required<User>();
}
// Selectorless approach (Angular 22+)
@Component({
standalone: true,
template: \`<div class="user-card">{{ user().name }}</div>\`
})
export class UserCardComponent {
user = input.required<User>();
}In Templates werden Selectorless Components über ihren Klassennamen anstelle eines benutzerdefinierten Selektor-Tags verwendet. Der Compiler stellt sicher, dass der Import existiert, und erkennt Fehler zur Build-Zeit statt zur Laufzeit.
Routing-Migration: Von Modulen zu loadComponent
Routing-Module erfordern manuelle Aufmerksamkeit, da das Schematic loadChildren-Modul-Imports nicht automatisch in loadComponent oder Routen-Level-loadChildren mit Standalone-Routen konvertiert.
Das alte Muster lud ganze Feature-Module:
const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.module')
.then(m => m.DashboardModule)
}
];Das Standalone-Äquivalent lädt einzelne Komponenten oder Routendateien direkt:
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'dashboard',
loadComponent: () => import('./dashboard/dashboard.component')
.then(c => c.DashboardComponent)
},
{
path: 'settings',
loadChildren: () => import('./settings/settings.routes')
.then(r => r.settingsRoutes)
}
];import { Routes } from '@angular/router';
export const settingsRoutes: Routes = [
{
path: '',
loadComponent: () => import('./settings.component')
.then(c => c.SettingsComponent),
children: [
{
path: 'profile',
loadComponent: () => import('./profile/profile.component')
.then(c => c.ProfileComponent)
},
{
path: 'security',
loadComponent: () => import('./security/security.component')
.then(c => c.SecurityComponent)
}
]
}
];loadComponent lädt eine einzelne Komponente per Lazy Loading. loadChildren mit einer Routendatei lädt einen ganzen Feature-Bereich per Lazy Loading. Beide erzeugen separate Chunks, die der Browser bei Bedarf abruft.
Bereit für deine Angular-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Umgang mit SharedModules und gemeinsamen Abhängigkeiten
SharedModules, die Sammelmodule, die häufig verwendete Komponenten, Direktiven und Pipes exportieren, sind das häufigste Hindernis bei der Migration. Das Schematic kann sie nicht automatisch entfernen, da mehrere Module sie importieren.
Die Lösung: gemeinsame Deklarationen einzeln in Standalone konvertieren und das SharedModule löschen, sobald nichts mehr es importiert.
// Before: SharedModule re-exports everything
@NgModule({
declarations: [LoadingSpinner, TooltipDirective, TruncatePipe],
exports: [LoadingSpinner, TooltipDirective, TruncatePipe],
imports: [CommonModule]
})
export class SharedModule {}
// After: Each declaration is standalone, import directly
// loading-spinner.component.ts
@Component({
selector: 'app-loading-spinner',
standalone: true,
template: \`<div class="spinner" role="status"></div>\`
})
export class LoadingSpinner {}Konsumenten importieren LoadingSpinner jetzt direkt anstelle des gesamten SharedModule. Der Bundler bezieht nur die spezifischen Komponenten ein, die jede Route benötigt.
Standalone-Only-Entwicklung erzwingen
Nach der Migration ist es entscheidend zu verhindern, dass neue NgModules wieder in die Codebasis einsickern. Angular bietet dafür eine TypeScript-Compiler-Option.
{
"angularCompilerOptions": {
"strictStandalone": true
}
}Mit aktiviertem strictStandalone führt jeder Versuch, eine nicht-standalone Komponente, Direktive oder Pipe zu erstellen, zu einem Kompilierungsfehler. Dies erzwingt die neue Architektur im gesamten Team. Die Angular-Forms-Migration folgt dem gleichen Muster, wobei Signal Forms in Angular 22 nun stabil sind.
Performance-Gewinne: Bundle-Größe und Lazy Loading
Der primäre Performance-Vorteil von Standalone Components ergibt sich aus granularem Lazy Loading. Mit NgModules funktionierte Lazy Loading auf Modulebene: der Import einer Komponente aus einem Modul zog jede Deklaration mit, die das Modul exportierte. Standalone Components durchbrechen diese Kopplung.
Ein Real-World-Benchmark mit einer mittelgroßen Enterprise-Anwendung (200+ Komponenten) zeigte:
| Metrik | NgModule-basiert | Standalone | Verbesserung |
|---|---|---|---|
| Initial Bundle | 485 KB | 218 KB | -55 % |
| Größter Lazy-Chunk | 142 KB | 38 KB | -73 % |
| Time to Interactive | 3,2 s | 1,8 s | -44 % |
| Build-Zeit (esbuild) | 12,4 s | 8,1 s | -35 % |
Diese Zahlen ergeben sich aus dem Wegfall des Overheads der Modulauflösung und der Möglichkeit für den Bundler, ungenutzte Exporte auf Komponentenebene statt auf Modulebene zu eliminieren.
Standalone Components mit Vitest testen
Unit-Tests werden mit Standalone Components deutlich einfacher. Angular 22 hat Vitest zum Standard-Testrunner gemacht und ersetzt damit Karma. Die TestBed-Konfiguration erfordert nicht länger den Import ganzer Module, um die Abhängigkeiten einer Komponente zu erfüllen.
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeroListComponent } from './hero-list.component';
import { HeroService } from '../services/hero.service';
import { of } from 'rxjs';
describe('HeroListComponent', () => {
let fixture: ComponentFixture<HeroListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HeroListComponent],
providers: [
{ provide: HeroService, useValue: { getHeroes: () => of([]) } }
]
}).compileComponents();
fixture = TestBed.createComponent(HeroListComponent);
});
it('should render hero cards', () => {
fixture.componentRef.setInput('heroes', mockHeroes);
fixture.detectChanges();
const cards = fixture.nativeElement.querySelectorAll('app-hero-card');
expect(cards.length).toBe(mockHeroes.length);
});
});Die Komponente landet direkt im imports-Array von TestBed.configureTestingModule. Alle deklarierten Abhängigkeiten sind bereits über das eigene imports aufgelöst, sodass keine zusätzlichen Modul-Imports nötig sind.
Häufige Migrationsfallen
Zirkuläre Imports zwischen Standalone Components. Wenn Komponente A Komponente B importiert und B wiederum A, wirft der TypeScript-Compiler einen Fehler wegen zirkulärer Abhängigkeit. Die Lösung: das gemeinsame Interface in eine separate Datei extrahieren oder forwardRef() als temporären Workaround verwenden, während die Abhängigkeitskette refaktoriert wird.
Drittanbieter-Bibliotheken nutzen noch NgModules. Viele Bibliotheken sind zu Standalone migriert, einige Legacy-Pakete exportieren jedoch weiterhin NgModules. Diese Module können direkt im imports-Array der Standalone-Komponente importiert werden, Angular unterstützt das Mischen von Standalone- und modulbasierten Imports.
Fehlende Provider nach Entfernung des AppModule. Services, die zuvor im providers-Array des Root-Moduls bereitgestellt wurden, müssen in das ApplicationConfig in app.config.ts verschoben werden oder providedIn: 'root' im @Injectable-Decorator verwenden. Routen-bezogene Services sollten das providers-Array in den Routenkonfigurationen nutzen.
Bei der Frage nach Standalone Components in einem Angular-Interview sollten drei Punkte im Fokus stehen: (1) Abhängigkeitsmanagement verlagert sich von der Modul- auf die Komponentenebene, (2) Tree-Shaking wird granular, und (3) der Test-Setup vereinfacht sich, da Komponenten ihre eigenen Imports deklarieren.
Quellen
- Angular v22 Release Notes - OnPush als Standard, Selectorless stabil, TypeScript 6 Voraussetzung
- Angular Standalone Migration Guide - Offizielle Dokumentation zum 3-Schritte-Schematic
- What's New in Angular 22 - Detaillierter Changelog mit Änderungen der Change-Detection-Strategie
- Selectorless Components RFC - Design-Begründung und Implementierungsstatus
Migration zu Standalone: Die wichtigsten Punkte für Angular 22
- Das offizielle Angular-CLI-Schematic automatisiert 80-90 % der Migration durch drei aufeinanderfolgende Durchgänge: Deklarationen konvertieren, Module entfernen, Bootstrap umstellen
- Routing-Module benötigen eine manuelle Konvertierung von
loadChildrenmit NgModules zuloadComponentoder Standalone-Routendateien - SharedModules sind das Hauptproblem: jede gemeinsame Deklaration einzeln in Standalone konvertieren und dann das Modul löschen
strictStandalonein der tsconfig aktivieren, um zu verhindern, dass nach der Migration neue NgModules eingeführt werden- Bundle-Größen sinken um 30-55 % dank komponentenbezogenem Tree-Shaking und granularem Lazy Loading mit
loadComponent - Unit-Tests werden mit Vitest als Standard-Runner einfacher: die Standalone-Komponente direkt in
TestBedohne Modulkonfiguration importieren - Angular 22s OnPush-Standard und Selectorless Components passen natürlich zur Standalone-Architektur für eine sauberere, wartbarere Codebasis
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Findest du den Bug in Angular?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 21. August 2026
Tags
Teilen
Verwandte Artikel

Angular-Entwickler-Jobs 2026: Wo suchen, Gehaltsübersicht und Vorbereitung
Der Arbeitsmarkt für Angular-Entwickler im September 2026: aktuelle Stellenangebote, Gehaltsstatistiken, Remote-Optionen und gezielte Interview-Vorbereitung.

Angular Signals und Computed 2026: Fein-granulare Reaktivität und Interview-Fragen
Ein tiefgehender Leitfaden zu Angular Signals, Computed Signals und dem reaktiven Modell in Angular 20+. Enthält Best Practices, RxJS-Interoperabilität und häufige technische Interview-Fragen.

Fortgeschrittene Angular Dependency Injection 2026: Provider, Tokens und Interview-Fragen
Angular bietet mit Providern, InjectionTokens und hierarchischen Injektoren ein mächtiges DI-System. Diese tiefgehende Analyse behandelt Provider-Strategien, die inject()-Funktion und praxisrelevante Interview-Fragen.