Skip to main content

Modules vs Standalone

Standalone Components (Modern - Default)

Standalone is the default and recommended approach for Angular 17+.

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
selector: 'app-user',
standalone: true,
imports: [CommonModule],
template: `<p>User component</p>`
})
export class UserComponent {}

Bootstrapping Standalone

// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient } from '@angular/common/http';
import { provideRouter } from '@angular/router';

bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(),
provideRouter(routes),
UserService
]
});

Standalone App Component

import { Component } from '@angular/core';
import { RouterOutlet, RouterLink } from '@angular/router';
import { CommonModule } from '@angular/common';

@Component({
selector: 'app-root',
standalone: true,
imports: [
CommonModule,
RouterOutlet,
RouterLink,
UserListComponent
],
template: `
<nav>
<a routerLink="/">Home</a>
<a routerLink="/users">Users</a>
</nav>
<router-outlet></router-outlet>
`
})
export class AppComponent {}

Standalone Routes

import { Routes } from '@angular/router';

export const routes: Routes = [
{
path: '',
loadComponent: () => import('./home/home.component')
.then(m => m.HomeComponent)
},
{
path: 'users',
loadComponent: () => import('./users/user-list.component')
.then(m => m.UserListComponent)
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
];

Lazy Loading Standalone

// admin.routes.ts
import { Routes } from '@angular/router';

export const ADMIN_ROUTES: Routes = [
{
path: '',
loadComponent: () => import('./admin-dashboard.component')
.then(m => m.AdminDashboardComponent)
},
{
path: 'users',
loadComponent: () => import('./admin-users.component')
.then(m => m.AdminUsersComponent)
}
];

Mixing Modules and Standalone

// Import standalone component in NgModule
@NgModule({
imports: [
BrowserModule,
UserComponent // Standalone component
],
declarations: [AppComponent]
})
export class AppModule {}

// Import NgModule in standalone component
@Component({
standalone: true,
imports: [
CommonModule,
ReactiveFormsModule // NgModule
]
})
export class FormComponent {}

Providers in Standalone

// Component-level
@Component({
standalone: true,
providers: [UserService] // New instance per component
})
export class UserComponent {}

// Application-level
bootstrapApplication(AppComponent, {
providers: [
{ provide: API_URL, useValue: 'https://api.example.com' },
UserService // Singleton
]
});

// Service-level
@Injectable({ providedIn: 'root' })
export class UserService {}

Common Imports

// Always import these when needed
import { CommonModule } from '@angular/common'; // *ngIf, *ngFor, pipes
import { FormsModule } from '@angular/forms'; // ngModel
import { ReactiveFormsModule } from '@angular/forms'; // Reactive forms
import { RouterModule } from '@angular/router'; // routerLink, router-outlet
import { HttpClientModule } from '@angular/common/http'; // Legacy
// Or
import { provideHttpClient } from '@angular/common/http'; // Standalone

Migration Tips

From NgModule to Standalone:

  1. Add standalone: true to @Component
  2. Add imports: [] array
  3. Import needed modules/components
  4. Remove from NgModule declarations
  5. Update routing to use loadComponent

Example:

// Before
@Component({
selector: 'app-user'
})

@NgModule({
declarations: [UserComponent]
})

// After
@Component({
selector: 'app-user',
standalone: true,
imports: [CommonModule]
})

Benefits of Standalone

  • Simpler - No NgModules needed
  • Faster - Better tree-shaking
  • Explicit - Clear dependencies
  • Lazy loading - Component-level
  • Less boilerplate
  • Better DX - Easier to understand

NgModules (Legacy)

Only use NgModules for legacy applications or when maintaining existing code.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

@NgModule({
declarations: [AppComponent, UserComponent],
imports: [BrowserModule],
providers: [UserService],
bootstrap: [AppComponent]
})
export class AppModule {}

When to Use Each

  • All new applications (Angular 17+)
  • Component libraries
  • Better tree-shaking and performance
  • Simpler, more explicit dependencies

Use NgModules (Legacy only)

  • Maintaining existing NgModule-based apps
  • Gradual migration from old codebase

Best Practices

  • Always use standalone for new Angular 17+ applications
  • Use bootstrapApplication() for app initialization
  • Use provideHttpClient() instead of HttpClientModule
  • Import only what you need in each component
  • Use loadComponent for lazy loading
  • Keep imports array organized and minimal