This question is about standalone migration execution: what moved from AppModule into bootstrapApplication or ApplicationConfig, component imports, and route providers, plus the provider-scope and route-wiring bugs teams hit during real migrations.
AppModule in the standalone era: what moved to components, bootstrapApplication, and route providers?
Migration map
Standalone Angular does not mean “move AppModule into AppComponent.” The production change is more specific: bootstrapping and global providers move to bootstrapApplication / ApplicationConfig, template dependencies move into each standalone component’s imports, and feature-scoped services move to route providers. The main pitfall is accidentally changing DI scope while you migrate.
Question intent | What to emphasize | What to de-emphasize |
|---|---|---|
This page: AppModule migration map | Where each AppModule responsibility moves: bootstrap, global providers, component imports, route-level providers, and DI scope checks. | Long debates about whether NgModules should exist conceptually. |
Related page: NgModules vs standalone boundaries | Compatibility cases: legacy declarables, | Step-by-step migration runbook from AppModule to standalone wiring. |
Scope guard
If the interviewer asks “Do NgModules still have value?”, that is the companion question: <a href="/angular/trivia/angular-ngmodules-vs-standalone">What problems do NgModules solve that standalone components don’t?</a>.
This page answers a different prompt: “I have an AppModule app; what exactly do I move, to where, and how do I avoid provider-scope regressions?”
AppModule responsibility (NgModule-era) | Standalone replacement (where it moved) | Concrete example |
|---|---|---|
Bootstrapping | main.ts uses |
|
Template dependency scope (CommonModule/Router/Material/etc.) | Standalone component |
|
Global providers via module imports | Provider functions + environment providers |
|
Feature modules for routing + lazy loading | Standalone routes + |
|
Legacy declarables packaging | Still NgModules (compat layer) | Standalone components import the legacy NgModule that exports them |
// NgModule-era (classic)
// main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule).catch(console.error);
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, HttpClientModule, RouterModule.forRoot([])],
bootstrap: [AppComponent]
})
export class AppModule {}
// Standalone-era (modern)
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(
withInterceptors([
// (req, next) => next(req)
])
),
provideAnimations()
]
};
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { appConfig } from './app.config';
bootstrapApplication(AppComponent, appConfig).catch(console.error);
// app.component.ts (standalone root)
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `<router-outlet></router-outlet>`
})
export class AppComponent {}
Interview hotspot: “Where do global things go now?” | Standalone answer |
|---|---|
RouterModule.forRoot(...) |
|
HttpClientModule |
|
BrowserAnimationsModule |
|
Feature-scoped providers | Route-level |
Module-only libraries (forRoot/NgModules) | Use |
// Route-level providers (feature scoping without a feature module)
import { Routes } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
export const routes: Routes = [
{
path: 'admin',
loadComponent: () => import('./admin/admin.page').then(m => m.AdminPage),
providers: [
// providers here are scoped to the admin route subtree
provideHttpClient()
]
}
];
Common standalone migration pitfall | What breaks | Fix |
|---|---|---|
Forgetting template imports (NgIf/NgFor/AsyncPipe/etc.) | Template compile errors: “Can’t bind to ...” / unknown directive/pipe | Import the standalone directive/pipe (e.g. |
Mixing module config patterns blindly | Duplicate providers / unexpected multiple instances | Prefer provider functions at bootstrap/route; use |
Assuming NgModules are “gone” | Legacy libs/declarables can’t be imported directly | Keep NgModules as a compatibility/container layer where needed |
Do you still need AppModule? | Answer |
|---|---|
Greenfield standalone app | Usually no — AppModule can disappear entirely. |
Incremental migration from a large NgModule app | Often yes (temporarily) — convert routes/components gradually while keeping AppModule as glue. |
Library ecosystem is NgModule-heavy (forRoot/exported modules) | You might still avoid AppModule, but you’ll rely on |
Practical scenario
Migrating a legacy Angular app to standalone components, you move imports and providers from AppModule into each feature component.
Common pitfalls
- Forgetting to add required imports to a standalone component.
- Provider scope changes causing unexpected singleton behavior.
- Mixing module-based libraries without clear boundaries.
Standalone simplifies bootstrapping but shifts responsibility to each component. Test DI scopes and route-level providers after migration.
Standalone doesn’t delete NgModules, but it does remove AppModule as the required wiring center. The senior answer here is operational: map each AppModule responsibility to its standalone destination, migrate providers deliberately, and verify DI scope at route/feature boundaries. Keep this answer migration-focused; keep compatibility/packaging trade-offs in the separate NgModules-vs-standalone discussion.
Use the relevant interview-question hub first, then move into a concrete study plan before targeted company sets.