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

TypeScript user-card.component.ts
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

TypeScript user-card.module.ts
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

AufgabeStandaloneNgModule
Anwendung startenbootstrapApplication(AppComponent)platformBrowserDynamic().bootstrapModule(AppModule)
Router konfigurierenprovideRouter(routes) in providersRouterModule.forRoot(routes) in imports
HTTP einrichtenprovideHttpClient()HttpClientModule importieren
Pipe/Direktive nutzenimports: [DatePipe] an der Componentdeclarations + imports: [CommonModule] im Modul
Component re-exportierennicht nötig (direkt importierbar)exports-Array
Lazy Loading per RouteloadComponent: () => import(...)loadChildren: () => import(...)
Globale ServicesprovidedIn: '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.

bash terminal
# 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

/ Weiter

Zurück zu Grundlagen

Zur Übersicht