Skip to content
Angular ng components 3 min read

Standalone Components

Standalone components let you build Angular UI without declaring anything in an NgModule. A standalone component manages its own dependencies through an imports array, so each piece of UI is self-contained and easy to reason about. Since Angular 19 they are the default, the CLI scaffolds them automatically, and NgModule is now an opt-in escape hatch rather than the starting point. This page explains how they work, why they replaced the module-centric model, and how to wire them together.

What “standalone” actually means

In the classic Angular model, a component could not be used until some NgModule declared it and re-exported whatever it depended on. That indirection was the source of countless “component is not a known element” errors. A standalone component removes the middleman: it declares its own template dependencies directly.

In Angular 17 and 18 you set the explicit flag standalone: true. From Angular 19 onward, standalone: true is the default value, so you simply omit it. If you ever need the legacy behaviour you must now write standalone: false.

import { Component } from '@angular/core';

@Component({
  selector: 'app-greeting',
  // standalone: true is the default in Angular 19+, no flag needed
  template: `<p>Hello, {{ name }}!</p>`,
})
export class GreetingComponent {
  name = 'Angular';
}

The declarations array of NgModule cannot hold standalone components. If you still maintain a module, list standalone components, directives, and pipes in imports instead.

Importing dependencies directly

Whatever a component’s template references, the component must import. That includes other standalone components, structural/attribute directives, pipes, and feature modules such as FormsModule. The built-in control flow (@if, @for, @switch) needs no import at all.

import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { GreetingComponent } from './greeting.component';

@Component({
  selector: 'app-profile',
  imports: [FormsModule, GreetingComponent],
  template: `
    <input [(ngModel)]="name" placeholder="Your name" />

    @if (name) {
      <app-greeting [name]="name" />
    } @else {
      <p>Type your name above.</p>
    }
  `,
})
export class ProfileComponent {
  name = '';
}

Because dependencies are local, the compiler can tree-shake unused code far more aggressively than it could with module-wide declarations.

Bootstrapping a standalone app

A standalone application skips the root AppModule entirely. You bootstrap the root component directly and configure providers in one place with bootstrapApplication.

import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes),
    provideHttpClient(),
  ],
}).catch((err) => console.error(err));

Application-wide singletons are supplied through provide* functions (or providedIn: 'root' services) rather than a module’s providers array.

Lazy loading and routing

Standalone components make route-level lazy loading a one-liner — no lazy module, no loadChildren boilerplate. Use loadComponent to defer a single component’s bundle.

import { Routes } from '@angular/router';

export const routes: Routes = [
  { path: '', component: ProfileComponent },
  {
    path: 'settings',
    loadComponent: () =>
      import('./settings/settings.component').then((m) => m.SettingsComponent),
  },
];

You can scope providers to a single route with the providers property on the route, giving you a lazy injector boundary without a module.

Generating one with the CLI

ng generate component user-card

Output:

CREATE src/app/user-card/user-card.component.html (24 bytes)
CREATE src/app/user-card/user-card.component.spec.ts (607 bytes)
CREATE src/app/user-card/user-card.component.ts (252 bytes)
CREATE src/app/user-card/user-card.component.css (0 bytes)

The generated .ts contains no standalone flag because it is already standalone by default.

Standalone vs NgModule at a glance

ConcernStandalone componentNgModule component
Dependency declarationimports on the componentdeclarations + imports on a module
ReusabilityImport the component anywhereRe-export it from a module first
BootstrapbootstrapApplication()platformBrowserDynamic().bootstrapModule()
Lazy loadingloadComponentloadChildren (lazy module)
Default sinceAngular 19Legacy, opt-in via standalone: false

Best practices

  • Omit the standalone flag in Angular 19+ — it is the default; only write standalone: false when you genuinely need module semantics.
  • Keep each component’s imports minimal and explicit so bundles stay small and dependencies stay obvious.
  • Prefer bootstrapApplication with provide* functions over a root AppModule for new apps.
  • Use loadComponent for route-level code splitting instead of creating lazy modules.
  • Provide services with providedIn: 'root' or route-scoped providers rather than reintroducing module providers.
  • Migrate existing apps incrementally with ng generate @angular/core:standalone, which automates the conversion in safe steps.
Last updated June 14, 2026
Was this helpful?