Open-Source Wikis

/

Angular

/

Features

/

Standalone components

angular/angular

Standalone components

Components, directives, and pipes that declare their own dependencies via imports: [...] instead of relying on an NgModule. Now the default for new code; the historical NgModule model is supported but no longer recommended.

What changed

Pre-standalone, every component was registered in an NgModule's declarations array. The module also declared providers (services), other modules to import, and exports for downstream modules to consume. Standalone components inverted the model: the component itself declares what it imports, and providers come from environment-level provideXxx functions.

// Standalone (current)
@Component({
  selector: 'my-page',
  imports: [CommonModule, RouterLink, MyChildComponent],
  template: `...`,
})
export class MyPage {}

bootstrapApplication(AppRoot, {
  providers: [
    provideRouter(routes),
    provideHttpClient(withFetch()),
    provideAnimationsAsync(),
  ],
});
// NgModule (legacy)
@NgModule({
  declarations: [MyPage, MyChildComponent],
  imports: [CommonModule, RouterModule.forRoot(routes)],
  providers: [...],
  bootstrap: [AppRoot],
})
export class AppModule {}
platformBrowserDynamic().bootstrapModule(AppModule);

How the compiler implements standalone

ngtsc's ComponentDecoratorHandler (packages/compiler-cli/src/ngtsc/annotations/component) checks the component's standalone field (defaults to true in newer projects, was false historically). When standalone:

  1. The compiler treats the imports array as the component's compilation scope.
  2. Each entry in imports is statically evaluated by the partial_evaluator. Acceptable forms: a directive class, another component class, a pipe class, or an NgModule class (whose exports flatten into the scope).
  3. The flattened scope replaces what an NgModule.declarations + imports would have provided.
  4. The output ɵcmp definition records the resolved scope so the runtime knows which directives to match against the template at runtime.

For DI, the runtime constructs an EnvironmentInjector chain at bootstrap; standalone components don't carry per-component environment providers (they use component-level providers: [...] for element-injector scoped providers).

provideXxx functions

Standalone-friendly libraries expose provide* factories that return EnvironmentProviders:

  • provideRouter(routes, …features)@angular/router.
  • provideHttpClient(…features)@angular/common/http.
  • provideAnimations() / provideAnimationsAsync() / provideNoopAnimations() — animations.
  • provideClientHydration(…features)@angular/core hydration.
  • provideZonelessChangeDetection() — opt into zoneless mode.
  • provideServerRendering()@angular/platform-server.
  • provideServiceWorker(filename, options)@angular/service-worker.
  • provideForms() (rare; reactive forms work without it in many cases).

The pattern: every feature that previously required XModule.forRoot(config) now has a provideX(config) function returning EnvironmentProviders.

Migrations

packages/core/schematics/migrations/standalone-migration/ — the canonical migration that:

  1. Marks every existing @Component/@Directive/@Pipe as standalone: true.
  2. Generates imports arrays from the matching NgModule.declarations + imports.
  3. Converts the bootstrap call from platformBrowserDynamic().bootstrapModule(AppModule) to bootstrapApplication(AppRoot, {providers}).
  4. Translates XModule.forRoot(...) calls into provideX(...) calls where a provide* equivalent exists.

The migration is the official way to move existing apps. New ng new projects start standalone by default.

Composition rules

  • Standalone components can import:
    • Other standalone components / directives / pipes.
    • NgModules (the module's exports flatten into the scope).
    • Arrays / functions returning the above (subject to static evaluation).
  • NgModules can declare and export standalone components as if they were declarations.
  • A standalone component cannot declare providers at the environment level — those come from bootstrapApplication's providers or provideXxx features.
  • Element-injector providers (component-scoped DI) are still configured via @Component({providers: [...]}).

Status

standalone: true is the default for ng new projects since v17. Documentation on angular.dev leads with the standalone API. The long-term plan is to deprecate and eventually remove @NgModule, but the timeline is intentionally gradual to give the ecosystem time to migrate. The tests in integration/ng-modules-importability/ make sure NgModules continue to work.

Where to start when modifying

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Standalone components – Angular wiki | Factory