Seit Angular v14 gibt es Standalone-Components als gleichwertige Alternative zu NgModules. Mit v17 wurde Standalone empfohlen, mit v19 ist es Default für neuen Code — standalone: true muss seither nicht mehr explizit gesetzt werden. NgModules sind aber kein Auslaufmodell: Sie sind weiterhin First-Class und werden vom Angular-Team aktiv gepflegt. Dieser Artikel vergleicht beide Welten fair und zeigt, wann welcher Ansatz seine Stärken ausspielt.
Standalone-Components und NgModules in einem Satz
Eine Standalone-Component trägt ihre Abhängigkeiten direkt im @Component-Dekorator: imports listet andere Components, Direktiven und Pipes; providers registriert Services lokal. Sie ist ohne Container-Konstrukt direkt importierbar.
Ein NgModule ist ein Container, der Components, Direktiven und Pipes über declarations bündelt, andere Module über imports einzieht, ein öffentliches API über exports definiert, Provider gemeinsam registriert und (im Root-Modul) den Bootstrap-Component über bootstrap festlegt.
Dasselbe Beispiel zweimal
Zur Verdeutlichung: eine simple UserCardComponent, die die DatePipe aus @angular/common nutzt — einmal Standalone, einmal innerhalb eines NgModules.
Standalone
import { Component, input } from '@angular/core';
import { DatePipe } from '@angular/common';
@Component({
selector: 'app-user-card',
// Ab v19 ist standalone: true Default und kann weggelassen werden.
imports: [DatePipe],
template: `
<article class="card">
<h3>{{ name() }}</h3>
<p>Beigetreten: {{ joined() | date }}</p>
</article>
`
})
export class UserCardComponent {
name = input.required<string>();
joined = input.required<Date>();
}NgModule-Variante
import { Component, NgModule, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-user-card',
standalone: false, // explizit, sonst wäre Standalone Default
template: `
<article class="card">
<h3>{{ name }}</h3>
<p>Beigetreten: {{ joined | date }}</p>
</article>
`
})
export class UserCardComponent {
@Input() name!: string;
@Input() joined!: Date;
}
@NgModule({
declarations: [UserCardComponent],
imports: [CommonModule], // bringt DatePipe mit
exports: [UserCardComponent] // öffentliches API des Moduls
})
export class UserCardModule {}Konzept-zu-Konzept-Übersetzung
| Aufgabe | Standalone | NgModule |
|---|---|---|
| Anwendung starten | bootstrapApplication(AppComponent) | platformBrowserDynamic().bootstrapModule(AppModule) |
| Router konfigurieren | provideRouter(routes) in providers | RouterModule.forRoot(routes) in imports |
| HTTP einrichten | provideHttpClient() | HttpClientModule importieren |
| Pipe/Direktive nutzen | imports: [DatePipe] an der Component | declarations + imports: [CommonModule] im Modul |
| Component re-exportieren | nicht nötig (direkt importierbar) | exports-Array |
| Lazy Loading per Route | loadComponent: () => import(...) | loadChildren: () => import(...) |
| Globale Services | providedIn: 'root' | providedIn: 'root' oder providers im Root-Modul |
Entscheidungshilfe
Standalone spielt seine Stärken aus, wenn
- du eine neue App oder ein neues Feature beginnst — weniger Boilerplate, klarer Scope
- eine Component isoliert verwendet wird (z. B. eine Dialog-Component)
- du einzelne Components lazy laden möchtest (
loadComponent) - du Richtung Mikro-Frontends oder gemeinsam genutzter Component-Bibliotheken arbeitest
- Testing im Vordergrund steht — weniger Setup, weniger Test-Module
NgModules sind weiterhin sinnvoll, wenn
- ein Feature aus vielen co-existierenden Components, Direktiven und Pipes besteht, die immer zusammen verwendet werden
- du in einer bestehenden Codebasis arbeitest und keine Migration erzwingen willst
- du Bibliotheken nutzt, die historisch ein
forRoot/forChild-Pattern voraussetzen - du Provider-Bündel zentral pflegen möchtest
Mit dem CLI-Schematic schrittweise migrieren
Angular liefert ein offizielles Schematic, das die Migration in drei sauberen Phasen durchführt. Zwischen jedem Schritt sollte die Anwendung gebaut und getestet werden.
# Phase 1: Components, Direktiven und Pipes auf Standalone umstellen
ng generate @angular/core:standalone
# → "Convert all components, directives and pipes to standalone"
# Phase 2: nicht mehr benötigte NgModules entfernen
ng generate @angular/core:standalone
# → "Remove unnecessary NgModule classes"
# Phase 3: Bootstrap auf bootstrapApplication umstellen
ng generate @angular/core:standalone
# → "Bootstrap the project using standalone APIs"Eine Migration ist nicht zwingend: Bestehender NgModule-Code läuft auf jeder aktuellen Angular-Version weiter, und der Mix-Modus (einige Bereiche Standalone, andere in NgModules) ist ausdrücklich unterstützt.
Was viele unterschätzen
Standalone-Components dürfen NgModules importieren
Im imports-Array einer Standalone-Component dürfen sowohl andere Standalone-Components als auch klassische NgModules stehen. Damit kannst du z. B. ein bestehendes Material-Feature-Modul direkt aus einer neuen Standalone-Component heraus nutzen — kein Entweder-Oder.
NgModules dürfen Standalone-Components importieren
Seit v15 lassen sich Standalone-Components, -Direktiven und -Pipes direkt im imports-Array eines NgModules verwenden — sie werden nicht in declarations aufgenommen. Das ist die zentrale Brücke für die schrittweise Migration grosser Codebasen.
CommonModule braucht es bei Standalone fast nie
Eine Standalone-Component importiert nur die Pipes und Direktiven, die sie wirklich nutzt — z. B. DatePipe oder AsyncPipe einzeln. Mit dem Built-in Control Flow (@if, @for) entfällt der Hauptzweck von CommonModule ohnehin.
providedIn: “root” funktioniert in beiden Welten
Service-DI ist orthogonal zur Component-Architektur. Ein Service mit { providedIn: “root“ } ist überall verfügbar — egal ob die konsumierende Component standalone oder Teil eines NgModules ist.
Lazy-Routes laden Standalone schneller
loadComponent lädt eine einzelne Component samt ihrer eigenen Imports. loadChildren zieht ein ganzes Modul mit allen Declarations und transitiven Imports. Bei kleinen Lazy-Zielen ergibt das spürbar kleinere Chunks und schnellere Time-to-Interactive.
Versions-Timeline: v14, v17, v19
Standalone-APIs sind ab v14 stabil, ab v17 die offizielle Empfehlung und ab v19 der Default — neu generierte Components werden ohne explizites standalone: true erzeugt. Wer NgModules braucht, setzt seither aktiv standalone: false.
Weiterführende Ressourcen
Externe Quellen
- Standalone components – Angular.dev
- Component decorator API – Angular.dev
- Standalone-Migration – Angular.dev
- NgModule decorator API – Angular.dev