Suggest an editImprove this articleRefine the answer for “What are modules in Angular and how are they used?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Angular modules (NgModules)** are containers that group components, directives, pipes, and services using the `@NgModule` decorator. ```ts @NgModule({ declarations: [MyComponent], // Components owned by this module imports: [CommonModule], // Other modules to use here exports: [MyComponent] // What other modules can access }) export class MyModule {} ``` **Key point:** `declarations` owns components locally; `exports` makes them available to other modules.Shown above the full answer for quick recall.Answer (EN)Image**Angular modules (NgModules)** are containers that group components, directives, pipes, and services, wiring them together through the `@NgModule` decorator. ## Theory ### TL;DR - An NgModule bundles related pieces: it declares what it owns, imports what it needs, and exports what others can use - Every app has one root module (`AppModule`) that bootstraps the app; all other modules are optional and domain-specific - Eager modules load upfront with the app; lazy modules load on demand, which cuts the initial bundle size - Angular 14+ introduced standalone components that skip NgModules entirely by declaring imports directly on the component - Decision rule: one root module plus feature modules per domain; lazy-load routes that are more than two screens deep ### Quick example This is the minimum wiring for any Angular app: ```ts // app.module.ts - bootstraps the application import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], // AppComponent belongs to this module imports: [BrowserModule], // BrowserModule provides DOM rendering + re-exports CommonModule providers: [], // App-wide services bootstrap: [AppComponent] // Component rendered on startup }) export class AppModule {} ``` Remove this file and the app won't start. These 10 lines are the minimum Angular needs to render anything. ### The five fields `@NgModule` takes one metadata object with five fields. Mixing them up is the most common mistake in Angular interviews: - `declarations` - components, directives, and pipes that *belong* to this module. Private unless exported. - `imports` - other modules whose exports you want to use here. - `exports` - your declarations exposed to any module that imports this one. - `providers` - services registered in this module's injector. - `bootstrap` - the root component rendered on startup. Only `AppModule` needs this. A component declared in one module is invisible to another unless it's exported from the first and that first module is imported by the second. This is the rule that trips most people up. ### Module types | Type | Purpose | |---|---| | `AppModule` | Root module, bootstraps the app | | Feature module | Isolates one domain: user, admin, checkout | | Shared module | Re-exports common utilities like `CommonModule`, shared pipes, UI components | | Core module | Singleton services, HTTP interceptors, app-level guards | | Lazy module | Feature loaded on demand via the router | ### How lazy loading works Add `loadChildren` to a route and Angular's router creates a separate JS chunk at build time. The chunk is only fetched when the user navigates to that path. ```ts // app-routing.module.ts const routes: Routes = [ { path: 'admin', // Webpack splits this into a separate admin.js chunk - fetched only on /admin loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) } ]; ``` The lazy module gets its own child injector, parented to the root injector. Services in the lazy module's `providers` array are scoped to that module only. I've seen this trip up teams who expected a lazy service to be a singleton but got a fresh instance on every navigation. ### Standalone components (Angular 14+) Standalone components declare their own imports directly, with no module required: ```ts // No module needed - the component manages its own dependencies @Component({ standalone: true, imports: [CommonModule, ReactiveFormsModule], template: `<form [formGroup]="form">...</form>` }) export class UserProfileComponent {} ``` For Angular 17+ projects, standalone is the default. NgModules remain the standard in large legacy codebases and in libraries like Angular Material. ### Common mistakes 1. **Declaring one component in two modules.** Angular throws at compile time: "Type X is part of the declarations of 2 modules." ```ts // Wrong: UserCard declared in both UserModule and SharedModule // Fix: declare in UserModule, re-export from SharedModule exports: [UserCard] ``` 2. **Using `BrowserModule` in a feature module.** It belongs only in `AppModule`. Feature modules use `CommonModule`. ```ts // Wrong in any module except AppModule: imports: [BrowserModule] // Fix: imports: [CommonModule] ``` 3. **Singleton services in a lazy module's `providers` array.** Each load creates a new instance, losing state. ```ts // Wrong - new UserService instance on every navigation: providers: [UserService] // Fix: @Injectable({ providedIn: 'root' }) export class UserService {} ``` 4. **Using `RouterModule.forRoot()` in a feature module.** Use `forRoot()` once in `AppModule`; use `forChild()` everywhere else. Using `forRoot()` twice silently breaks route guards. 5. **Forgetting `CommonModule` in feature modules.** `*ngIf` and `*ngFor` stop working with a confusing error message about unknown directives. ### Real-world usage - **Angular Material**: separate module per component (`MatButtonModule`, `MatDialogModule`). Import only what you actually use. - **NGXS / NgRx**: call `.forRoot()` in `AppModule`, `.forFeature()` in lazy modules. Mixing these breaks state. - **Nx workspace**: auto-generates lazy feature modules per library. Each lib maps to one bounded context. - **Core vs Shared pattern**: `CoreModule` holds `HTTP_INTERCEPTORS`, `AuthGuard`, global error handlers (imported once in `AppModule`). `SharedModule` holds `CommonModule`, shared pipes, UI wrapper components (imported in every feature module). ### Follow-up questions **Q:** What is the difference between `declarations`, `imports`, and `exports` in `@NgModule`? **A:** `declarations` registers components, pipes, and directives as owned by this module (local scope only). `imports` pulls in another module's exported items for use here. `exports` makes your declarations visible to any module that imports yours. **Q:** Why use `CommonModule` in feature modules instead of `BrowserModule`? **A:** `BrowserModule` registers app-level providers that must only exist once. Using it in a feature module causes Angular to throw "BrowserModule has already been loaded." Feature modules get `*ngIf` and `*ngFor` by importing `CommonModule` directly. **Q:** How does lazy loading reduce bundle size? **A:** `loadChildren` with a dynamic `import()` tells webpack to create a separate chunk for that module. The chunk is excluded from the main bundle and fetched only when the router activates that route. **Q:** When would you skip NgModules entirely? **A:** In Angular 15+ projects using standalone components. Call `bootstrapApplication()` with an `ApplicationConfig`, and each component declares its own imports. No `AppModule` needed. **Q:** (Senior) How does the injector hierarchy behave with lazy modules? **A:** A lazy module creates a child injector parented to the root injector. When a component inside the lazy module requests a service, Angular searches up: child injector first, then root. A service in the lazy module's `providers` is a different instance from the one in the root. That is why `providedIn: 'root'` is the safe default for anything that should be a singleton. ## Examples ### Root module setup The minimum to start an Angular app: ```ts // app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, // Only here - sets up DOM rendering, re-exports CommonModule AppRoutingModule // Router setup ], bootstrap: [AppComponent] }) export class AppModule {} ``` `BrowserModule` goes here and nowhere else. Any feature module that also imports it will break the app on startup. ### Feature module with routing A user profile domain with reactive forms, following the Angular Material docs structure: ```ts // user.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ReactiveFormsModule } from '@angular/forms'; import { UserProfileComponent } from './user-profile.component'; import { UserRoutingModule } from './user-routing.module'; @NgModule({ declarations: [UserProfileComponent], imports: [ CommonModule, // *ngIf, *ngFor, AsyncPipe ReactiveFormsModule, // FormGroup, FormControl UserRoutingModule // Registers /profile route ] // No exports - this module is self-contained }) export class UserModule {} // user-routing.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { path: 'profile', component: UserProfileComponent } ]; @NgModule({ imports: [RouterModule.forChild(routes)], // forChild, not forRoot exports: [RouterModule] }) export class UserRoutingModule {} ``` `forChild` is not optional here. Using `forRoot` in a feature module causes route guard failures that are hard to trace back to this line. ### Shared module pattern When multiple feature modules need the same pipes and components: ```ts // shared.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { DateFormatPipe } from './pipes/date-format.pipe'; import { LoadingSpinnerComponent } from './loading-spinner.component'; @NgModule({ declarations: [DateFormatPipe, LoadingSpinnerComponent], imports: [CommonModule], exports: [ CommonModule, // Feature modules get *ngIf etc. for free DateFormatPipe, LoadingSpinnerComponent ] }) export class SharedModule {} // Any feature module now gets all three with one line: // imports: [SharedModule] ``` This is the real-world CoreModule / SharedModule split you will find in most production Angular repos. `CoreModule` holds singletons. `SharedModule` holds reusable UI pieces.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.