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
declarationsarray ofNgModulecannot hold standalone components. If you still maintain a module, list standalone components, directives, and pipes inimportsinstead.
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
| Concern | Standalone component | NgModule component |
|---|---|---|
| Dependency declaration | imports on the component | declarations + imports on a module |
| Reusability | Import the component anywhere | Re-export it from a module first |
| Bootstrap | bootstrapApplication() | platformBrowserDynamic().bootstrapModule() |
| Lazy loading | loadComponent | loadChildren (lazy module) |
| Default since | Angular 19 | Legacy, opt-in via standalone: false |
Best practices
- Omit the
standaloneflag in Angular 19+ — it is the default; only writestandalone: falsewhen you genuinely need module semantics. - Keep each component’s
importsminimal and explicit so bundles stay small and dependencies stay obvious. - Prefer
bootstrapApplicationwithprovide*functions over a rootAppModulefor new apps. - Use
loadComponentfor route-level code splitting instead of creating lazy modules. - Provide services with
providedIn: 'root'or route-scopedprovidersrather than reintroducing module providers. - Migrate existing apps incrementally with
ng generate @angular/core:standalone, which automates the conversion in safe steps.