Standalone components provide a simplified way to build Angular applications. Standalone components, directives, and pipes aim to streamline the authoring experience by reducing the need for NgModule
s. Existing applications can optionally and incrementally adopt the new standalone style without any breaking changes.
This schematic helps to transform components, directive and pipes in existing projects to become standalone. The schematic aims to transform as much code as possible automatically, but it may require some manual fixes by the project author.
Run the schematic using the following command:
ng generate @angular/core:standalone
Before updating
Before using the schematic, please ensure that the project:
- Is using Angular 15.2.0 or later.
- Builds without any compilation errors.
- Is on a clean Git branch and all work is saved.
Schematic options
Option | Details |
---|---|
mode |
The transformation to perform. See Migration modes below for details on the available options. |
path |
The path to migrate, relative to the project root. You can use this option to migrate sections of your project incrementally. |
Migrations steps
The migration process is composed of three steps. You'll have to run it multiple times and check manually that the project builds and behaves as expected.
Note: While the schematic can automatically update most code, some edge cases require developer intervention. You should plan to apply manual fixes after each step of the migration. Additionally, the new code generated by the schematic may not match your code's formatting rules.
Run the migration in the order listed below, verifying that your code builds and runs between each step:
- Run
ng g @angular/core:standalone
and select "Convert all components, directives and pipes to standalone" - Run
ng g @angular/core:standalone
and select "Remove unnecessary NgModule classes" - Run
ng g @angular/core:standalone
and select "Bootstrap the project using standalone APIs" - Run any linting and formatting checks, fix any failures, and commit the result
After the migration
Congratulations, your application has been converted to standalone 🎉. These are some optional follow-up steps you may want to take now:
- Find and remove any remaining
NgModule
declarations: since the "Remove unnecessary NgModules" step cannot remove all modules automatically, you may have to remove the remaining declarations manually. - Run the project's unit tests and fix any failures.
- Run any code formatters, if the project uses automatic formatting.
- Run any linters in your project and fix new warnings. Some linters support a
--fix
flag that may resolve some of your warnings automatically.
Migration modes
The migration has the following modes:
- Convert declarations to standalone.
- Remove unnecessary NgModules.
- Switch to standalone bootstrapping API. You should run these migrations in the order given.
Convert declarations to standalone
In this mode, the migration converts all components, directives and pipes to standalone by removing standalone: false
and adding dependencies to their imports
array.
HELPFUL: The schematic ignores NgModules which bootstrap a component during this step because they are likely root modules used by bootstrapModule
rather than the standalone-compatible bootstrapApplication
. The schematic converts these declarations automatically as a part of the "Switch to standalone bootstrapping API" step.
Before:
// shared.module.ts@NgModule({ imports: [CommonModule], declarations: [GreeterComponent], exports: [GreeterComponent]})export class SharedModule {}
// greeter.component.ts@Component({ selector: 'greeter', template: '<div *ngIf="showGreeting">Hello</div>', standalone: false,})export class GreeterComponent { showGreeting = true;}
After:
// shared.module.ts@NgModule({ imports: [CommonModule, GreeterComponent], exports: [GreeterComponent]})export class SharedModule {}
// greeter.component.ts@Component({ selector: 'greeter', template: '<div *ngIf="showGreeting">Hello</div>', imports: [NgIf]})export class GreeterComponent { showGreeting = true;}
Remove unnecessary NgModules
After converting all declarations to standalone, many NgModules can be safely removed. This step deletes such module declarations and as many corresponding references as possible. If the migration cannot delete a reference automatically, it leaves the following TODO comment so that you can delete the NgModule manually:
/* TODO(standalone-migration): clean up removed NgModule reference manually */
The migration considers a module safe to remove if that module:
- Has no
declarations
. - Has no
providers
. - Has no
bootstrap
components. - Has no
imports
that reference aModuleWithProviders
symbol or a module that can't be removed. - Has no class members. Empty constructors are ignored.
Before:
// importer.module.ts@NgModule({ imports: [FooComponent, BarPipe], exports: [FooComponent, BarPipe]})export class ImporterModule {}
After:
// importer.module.ts// Does not exist!
Switch to standalone bootstrapping API
This step converts any usages of bootstrapModule
to the new, standalone-based bootstrapApplication
. It also removes standalone: false
from the root component and deletes the root NgModule. If the root module has any providers
or imports
, the migration attempts to copy as much of this configuration as possible into the new bootstrap call.
Before:
// ./app/app.module.tsimport { NgModule } from '@angular/core';import { AppComponent } from './app.component';@NgModule({ declarations: [AppComponent], bootstrap: [AppComponent]})export class AppModule {}
// ./app/app.component.ts@Component({ selector: 'app', template: 'hello', standalone: false,})export class AppComponent {}
// ./main.tsimport { platformBrowser } from '@angular/platform-browser';import { AppModule } from './app/app.module';platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
After:
// ./app/app.module.ts// Does not exist!
// ./app/app.component.ts@Component({ selector: 'app', template: 'hello'})export class AppComponent {}
// ./main.tsimport { bootstrapApplication } from '@angular/platform-browser';import { AppComponent } from './app/app.component';bootstrapApplication(AppComponent).catch(e => console.error(e));
Common problems
Some common problems that may prevent the schematic from working correctly include:
- Compilation errors - if the project has compilation errors, Angular cannot analyze and migrate it correctly.
- Files not included in a tsconfig - the schematic determines which files to migrate by analyzing your project's
tsconfig.json
files. The schematic excludes any files not captured by a tsconfig. - Code that cannot be statically analyzed - the schematic uses static analysis to understand your code and determine where to make changes. The migration may skip any classes with metadata that cannot be statically analyzed at build time.
Limitations
Due to the size and complexity of the migration, there are some cases that the schematic cannot handle:
- Because unit tests are not ahead-of-time (AoT) compiled,
imports
added to components in unit tests might not be entirely correct. - The schematic relies on direct calls to Angular APIs. The schematic cannot recognize custom wrappers around Angular APIs. For example, if there you define a custom
customConfigureTestModule
function that wrapsTestBed.configureTestingModule
, components it declares may not be recognized.