# Angular Standalone Components: Migration und Best Practices in 2026 > Vollständiger Leitfaden zur Migration von Angular-Anwendungen von NgModules zu Standalone-Komponenten. Behandelt die offizielle 3-stufige CLI-Migration, Lazy Loading, Routing und Best Practices für Angular 21. - Published: 2026-04-21 - Updated: 2026-04-27 - Author: SharpSkill - Tags: angular, standalone components, migration, angular 21, ngmodule, lazy loading - Reading time: 9 min --- 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 21 die zoneless Change Detection gefestigt hat, ist die Migration alter modulbasierter Codebasen sowohl unkompliziert als auch wirkungsvoll geworden. > **Wichtigste Erkenntnis** > > 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. ```typescript // hero-list.component.ts import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HeroCardComponent } from './hero-card.component'; import { SearchPipe } from '../pipes/search.pipe'; @Component({ selector: 'app-hero-list', standalone: true, imports: [CommonModule, HeroCardComponent, SearchPipe], template: `
@for (hero of heroes | search:query; track hero.id) { }
` }) export class HeroListComponent { heroes = signal([]); 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. ## 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. ```bash # Step 1: Convert all declarations to standalone ng g @angular/core:standalone --path=src/app ``` Wä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. ```bash # Step 2: Remove empty NgModules ng g @angular/core:standalone --path=src/app ``` Wä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. ### Schritt 3: Auf Standalone-Bootstrap umstellen Der letzte Durchgang ersetzt das Root-NgModule durch Angulars `bootstrapApplication`-API und konvertiert die Root-Komponente in Standalone. ```typescript // main.ts (after migration) 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)); ``` ```typescript // app.config.ts import { ApplicationConfig } 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: [ 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. ## 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: ```typescript // app.routes.ts (before) const routes: Routes = [ { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module') .then(m => m.DashboardModule) } ]; ``` Das Standalone-Äquivalent lädt einzelne Komponenten oder Routendateien direkt: ```typescript // app.routes.ts (after) 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) } ]; ``` ```typescript // settings/settings.routes.ts 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. ## 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. ```typescript // 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: `
` }) 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. ```json // tsconfig.json { "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. ## 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 testen Unit-Tests werden mit Standalone Components deutlich einfacher. Die `TestBed`-Konfiguration erfordert nicht länger den Import ganzer Module, um die Abhängigkeiten einer Komponente zu erfüllen. ```typescript // hero-list.component.spec.ts import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HeroListComponent } from './hero-list.component'; import { HeroService } from '../services/hero.service'; describe('HeroListComponent', () => { let fixture: ComponentFixture; 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. ## Fazit - 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 `loadChildren` mit NgModules zu `loadComponent` oder Standalone-Routendateien - SharedModules sind das Hauptproblem — jede gemeinsame Deklaration einzeln in Standalone konvertieren und dann das Modul löschen - `strictStandalone` in 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 drastisch einfacher — die Standalone-Komponente direkt in `TestBed` ohne Modulkonfiguration importieren - Angular 21s zoneless Change Detection und [signal-basierte Reaktivität](/technologies/angular/interview-questions/angular-signals) passen perfekt zur Standalone-Architektur für maximale Performance --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/angular/angular-standalone-components-migration-best-practices