Skip to main content
NgModules are TypeScript classes decorated with @NgModule that organize Angular applications into cohesive blocks of functionality. While standalone components are now the recommended approach, NgModules are still widely used in existing applications.
Standalone components are the recommended approach for new Angular applications. NgModules are considered legacy but remain fully supported.

What is an NgModule?

An NgModule is a container for components, directives, pipes, and services that work together as a functional unit:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { UserListComponent } from './user-list.component';
import { UserService } from './user.service';

@NgModule({
  declarations: [    // Components, directives, and pipes
    AppComponent,
    UserListComponent
  ],
  imports: [         // Other modules
    BrowserModule
  ],
  providers: [       // Services
    UserService
  ],
  bootstrap: [       // Root component(s)
    AppComponent
  ]
})
export class AppModule { }

NgModule Metadata

The @NgModule decorator accepts metadata:

declarations

Components, directives, and pipes that belong to this module:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UserListComponent } from './user-list.component';
import { UserDetailComponent } from './user-detail.component';
import { HighlightDirective } from './highlight.directive';
import { TruncatePipe } from './truncate.pipe';

@NgModule({
  declarations: [
    UserListComponent,
    UserDetailComponent,
    HighlightDirective,
    TruncatePipe
  ],
  imports: [CommonModule],
  exports: [UserListComponent, UserDetailComponent]
})
export class UserModule { }
Each component, directive, or pipe can only be declared in ONE NgModule. Declaring in multiple modules will cause a compilation error.

imports

Other modules whose exported classes are needed:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';

@NgModule({
  imports: [
    CommonModule,           // ngIf, ngFor, etc.
    FormsModule,            // ngModel
    ReactiveFormsModule,    // Reactive forms
    HttpClientModule,       // HTTP client
    RouterModule.forChild(routes)
  ]
})
export class FeatureModule { }

exports

Make components, directives, and pipes available to other modules:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ButtonComponent } from './button.component';
import { CardComponent } from './card.component';
import { HighlightDirective } from './highlight.directive';

@NgModule({
  declarations: [
    ButtonComponent,
    CardComponent,
    HighlightDirective,
    InternalComponent  // Not exported - only for internal use
  ],
  imports: [CommonModule],
  exports: [
    // Export own declarations
    ButtonComponent,
    CardComponent,
    HighlightDirective,
    // Re-export other modules
    CommonModule
  ]
})
export class SharedModule { }

providers

Services available to the module’s injector:
import { NgModule } from '@angular/core';
import { UserService } from './user.service';
import { AuthService } from './auth.service';
import { LoggerService } from './logger.service';
import { API_URL } from './tokens';

@NgModule({
  providers: [
    UserService,
    AuthService,
    LoggerService,
    { provide: API_URL, useValue: 'https://api.example.com' }
  ]
})
export class CoreModule { }

bootstrap

The root component(s) to bootstrap:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  bootstrap: [AppComponent]  // Only in root module
})
export class AppModule { }

Common Module Patterns

Root Module (AppModule)

The main application module:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
import { SharedModule } from './shared/shared.module';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    HttpClientModule,
    CoreModule,
    SharedModule,
    AppRoutingModule  // Should be last
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Core Module

Singleton services and app-wide components:
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AuthService } from './auth.service';
import { LoggerService } from './logger.service';
import { HeaderComponent } from './header.component';
import { FooterComponent } from './footer.component';

@NgModule({
  declarations: [HeaderComponent, FooterComponent],
  imports: [CommonModule],
  exports: [HeaderComponent, FooterComponent],
  providers: [AuthService, LoggerService]
})
export class CoreModule {
  constructor(@Optional() @SkipSelf() parentModule?: CoreModule) {
    if (parentModule) {
      throw new Error('CoreModule is already loaded. Import it only in AppModule');
    }
  }
}

Shared Module

Reusable components, directives, and pipes:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ButtonComponent } from './button.component';
import { CardComponent } from './card.component';
import { LoaderComponent } from './loader.component';
import { TruncatePipe } from './truncate.pipe';
import { HighlightDirective } from './highlight.directive';

@NgModule({
  declarations: [
    ButtonComponent,
    CardComponent,
    LoaderComponent,
    TruncatePipe,
    HighlightDirective
  ],
  imports: [CommonModule, FormsModule],
  exports: [
    // Export own declarations
    ButtonComponent,
    CardComponent,
    LoaderComponent,
    TruncatePipe,
    HighlightDirective,
    // Re-export common modules
    CommonModule,
    FormsModule
  ]
})
export class SharedModule { }

Feature Module

Domain-specific functionality:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { SharedModule } from '../shared/shared.module';
import { UserListComponent } from './user-list.component';
import { UserDetailComponent } from './user-detail.component';
import { UserService } from './user.service';

const routes: Routes = [
  { path: '', component: UserListComponent },
  { path: ':id', component: UserDetailComponent }
];

@NgModule({
  declarations: [
    UserListComponent,
    UserDetailComponent
  ],
  imports: [
    CommonModule,
    SharedModule,
    RouterModule.forChild(routes)
  ],
  providers: [UserService]
})
export class UserModule { }

Lazy Loading Modules

Load modules on demand:
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'users',
    loadChildren: () => import('./user/user.module').then(m => m.UserModule)
  },
  {
    path: 'products',
    loadChildren: () => import('./product/product.module').then(m => m.ProductModule)
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

ModuleWithProviders

Configure module with runtime values:
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CONFIG_TOKEN, ModuleConfig } from './config';
import { ConfigurableService } from './configurable.service';

@NgModule({
  imports: [CommonModule]
})
export class ConfigurableModule {
  static forRoot(config: ModuleConfig): ModuleWithProviders<ConfigurableModule> {
    return {
      ngModule: ConfigurableModule,
      providers: [
        ConfigurableService,
        { provide: CONFIG_TOKEN, useValue: config }
      ]
    };
  }

  static forChild(): ModuleWithProviders<ConfigurableModule> {
    return {
      ngModule: ConfigurableModule,
      providers: []
    };
  }
}

// Usage in AppModule
@NgModule({
  imports: [
    ConfigurableModule.forRoot({ apiUrl: 'https://api.example.com' })
  ]
})
export class AppModule { }

// Usage in feature module
@NgModule({
  imports: [
    ConfigurableModule.forChild()
  ]
})
export class FeatureModule { }

Migration to Standalone

Migrate from NgModules to standalone components:
// Before (NgModule)
@NgModule({
  declarations: [UserComponent],
  imports: [CommonModule, FormsModule],
  exports: [UserComponent]
})
export class UserModule { }

// After (Standalone)
@Component({
  selector: 'app-user',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `...`
})
export class UserComponent { }
Use Angular CLI to help with migration:
ng generate @angular/core:standalone

Common Mistakes

Avoid these common NgModule mistakes:
  1. Declaring in multiple modules - Each component can only be in ONE NgModule
  2. Not importing CommonModule - Feature modules need CommonModule, not BrowserModule
  3. Importing modules in wrong order - RouterModule should typically be last
  4. Providing services in feature modules - Use providedIn: 'root' instead
  5. Re-importing HttpClientModule - Import only once in AppModule or CoreModule

Best Practices

  1. Consider standalone first - For new applications
  2. One core module - Import once in AppModule
  3. Shared module for common items - Import in feature modules
  4. Feature modules for domains - Organize by business domain
  5. Lazy load feature modules - Improve initial load time
  6. Use forRoot/forChild pattern - For configurable modules

Next Steps

Components

Learn about standalone components

Dependency Injection

Understand Angular’s DI system

Build docs developers (and LLMs) love