1. What is an NgModule
An NgModule groups related components, directives, and pipes together. It was the primary way to structure Angular apps before standalone components arrived in Angular 14.
NgModules are not deprecated — they are still fully supported in Angular 21. Only platformBrowserDynamic (the old bootstrap function) was deprecated. The recommended path for new projects is standalone components, but NgModules remain valid for existing codebases.
2. NgModule Anatomy
The @NgModule decorator accepts five main properties:
declarations
Non-standalone components, directives, and pipes owned by this module
imports
Other NgModules or standalone components/pipes this module needs
exports
What this module exposes to other modules that import it
providers
Services to register (prefer providedIn: 'root' on the service itself)
bootstrap
The root component Angular renders on startup — root module only
@NgModule({
declarations: [App],
bootstrap: [App],
imports: [BrowserModule, CommonModule, Tasks, NewTask, Task, User, Header, RouterOutlet],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode
3. declarations vs imports
This is the most common source of confusion when mixing modules with standalone components.
-
declarations— only for non-standalone components (standalone: false). They are private to this module unless explicitly exported. -
imports— for other NgModules and for standalone components, directives, and pipes (the default since Angular 17).
Putting a standalone component in declarations causes a compile error. Putting a non-standalone component in imports also causes an error.
// non-standalone component — belongs in declarations
@Component({
standalone: false,
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}
Enter fullscreen mode Exit fullscreen mode
// standalone component (Angular 17+ default) — belongs in imports, not declarations
@Component({
selector: 'app-header',
templateUrl: './header.html',
})
export class Header {}
Enter fullscreen mode Exit fullscreen mode
@NgModule({
declarations: [App], // non-standalone
imports: [BrowserModule, Header, RouterOutlet], // standalone components go here
bootstrap: [App],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode
A non-standalone component also cannot have an imports array in @Component — it delegates all imports to its module.
4. BrowserModule vs CommonModule
Module
Use when
BrowserModule
Root module only — sets up the browser platform, includes CommonModule internally
CommonModule
Feature modules — provides NgIf, NgFor, NgClass, pipes, etc.
Never import BrowserModule in a feature module — import CommonModule instead. Importing BrowserModule twice throws a runtime error.
5. Bootstrapping with NgModule
Use platformBrowser().bootstrapModule() from @angular/platform-browser. The older platformBrowserDynamic from @angular/platform-browser-dynamic was deprecated in Angular 21.
// main.ts
import { platformBrowser } from '@angular/platform-browser';
import { AppModule } from './app/app.module';
platformBrowser().bootstrapModule(AppModule)
.catch((err: unknown) => console.error(err));
Enter fullscreen mode Exit fullscreen mode
6. Feature Modules
Feature modules group a slice of the app (e.g. tasks, users) to keep AppModule small. They export only what the rest of the app needs.
// tasks/tasks.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TasksComponent } from './tasks.component';
@NgModule({
declarations: [TasksComponent],
imports: [CommonModule],
exports: [TasksComponent],
})
export class TasksModule {}
Enter fullscreen mode Exit fullscreen mode
// app.module.ts
@NgModule({
declarations: [App],
imports: [BrowserModule, TasksModule],
bootstrap: [App],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode
7. Lazy-Loaded Feature Modules
Lazy loading defers loading a module’s bundle until the user navigates to its route. Configure it with loadChildren in the router:
// app.routes.ts
export const routes: Routes = [
{
path: 'tasks',
loadChildren: () =>
import('./tasks/tasks.module').then((m) => m.TasksModule),
},
];
Enter fullscreen mode Exit fullscreen mode
// tasks/tasks.module.ts — use forChild, not forRoot
@NgModule({
declarations: [TasksComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: TasksComponent }]),
],
})
export class TasksModule {}
Enter fullscreen mode Exit fullscreen mode
The root module uses RouterModule.forRoot(routes). Feature modules use RouterModule.forChild(routes).
8. NgModule vs Standalone
NgModule Standalone Grouping Explicit module files No module needed Sharing Export from module, import the module Import the component directly Lazy loadingloadChildren
loadComponent
Recommended for new projects
No
Yes
Deprecated
No
—
NgModules are still valid and fully supported. Understanding them is essential for working with existing Angular codebases.
답글 남기기