Standalone Components
What Are Standalone Components?
Standalone components (stable in Angular 15) allow you to build Angular applications without NgModules. They are self-contained units that declare their own dependencies.
Key Features
1. standalone: true Flag
import { Component } from "@angular/core";
import { CommonModule } from "@angular/common";
@Component({
selector: "app-user-profile",
standalone: true,
imports: [CommonModule], // Direct imports instead of NgModule
template: `<h1>{{ userName }}</h1>`,
})
export class UserProfileComponent {
userName = "John Doe";
}
2. Direct Imports
Components can import other standalone components, directives, pipes, or NgModules directly:
@Component({
standalone: true,
imports: [
CommonModule, // NgModule
MatButtonModule, // NgModule
UserCardComponent, // Standalone component
HighlightDirective, // Standalone directive
DateFormatPipe // Standalone pipe
],
// ...
})
3. Bootstrap Without NgModule
Old Way (NgModule):
// main.ts
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { AppModule } from "./app/app.module";
platformBrowserDynamic().bootstrapModule(AppModule);
New Way (Standalone):
// main.ts
import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app/app.component";
bootstrapApplication(AppComponent);
Benefits
Tree-Shaking
- Only import what you need
- Smaller bundle sizes
- Better code splitting
Simplified Architecture
- No NgModule boilerplate
- Clearer dependencies
- Easier to understand component requirements
Better Developer Experience
- Less configuration
- Faster development
- Reduced cognitive overhead
Improved Testing
// Simpler component testing
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { UserProfileComponent } from "./user-profile.component";
describe("UserProfileComponent", () => {
let component: UserProfileComponent;
let fixture: ComponentFixture<UserProfileComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [UserProfileComponent], // Just import the standalone component!
});
fixture = TestBed.createComponent(UserProfileComponent);
component = fixture.componentInstance;
});
it("should create", () => {
expect(component).toBeTruthy();
});
});
Common Patterns
Standalone Component with Services
import { Component, inject } from "@angular/core";
import { UserService } from "./user.service";
@Component({
selector: "app-user-list",
standalone: true,
template: `
@for (user of users; track user.id) {
<div>{{ user.name }}</div>
}
`,
})
export class UserListComponent {
private userService = inject(UserService);
users = this.userService.getUsers();
}
Providing Services in Standalone
import { Component } from "@angular/core";
import { provideHttpClient } from "@angular/common/http";
@Component({
standalone: true,
providers: [UserService], // Component-level providers
// ...
})
export class UserListComponent {}
Migration Strategy
Gradual Migration
You can mix NgModules and standalone components:
// Old NgModule importing standalone component
@NgModule({
imports: [
StandaloneUserCardComponent, // Import standalone in NgModule
],
declarations: [OldUserListComponent],
})
export class UserModule {}
// Standalone component importing NgModule
@Component({
standalone: true,
imports: [
UserModule, // Import NgModule in standalone
],
})
export class NewDashboardComponent {}
Interview Tips
Q: What problem do standalone components solve? A: They eliminate NgModule boilerplate, improve tree-shaking, simplify dependencies, and reduce bundle size while making the architecture more intuitive.
Q: Can you mix standalone and NgModule-based components? A: Yes! Angular 15+ supports both, allowing gradual migration.
Q: When should you use standalone components? A: For all new Angular 15+ projects. They're simpler, more maintainable, and will be the recommended approach going forward.