Bonnes pratiques angular
/SKILLPerformance Optimization and Best Practices Guide for Angular. Use this guide when writing, reviewing, or refactoring
--- name: angular-best-practices description: "Angular performance optimization and best practices guide. Use when writing, reviewing, or refactoring Angular code for optimal performance, bundle size, and rendering efficiency." risk: safe source: self date_added: "2026-02-27" --- # Angular Best Practices Comprehensive performance optimization guide for Angular applications. Contains prioritized rules for eliminating performance bottlenecks, optimizing bundles, and improving rendering. ## When to Use Reference these guidelines when: - Writing new Angular components or pages - Implementing data fetching patterns - Reviewing code for performance issues - Refactoring existing Angular code - Optimizing bundle size or load times - Configuring SSR/hydration --- ## Rule Categories by Priority | Priority | Category | Impact | Focus | | -------- | --------------------- | ---------- | ------------------------------- | | 1 | Change Detection | CRITICAL | Signals, OnPush, Zoneless | | 2 | Async Waterfalls | CRITICAL | RxJS patterns, SSR preloading | | 3 | Bundle Optimization | CRITICAL | Lazy loading, tree shaking | | 4 | Rendering Performance | HIGH | @defer, trackBy, virtualization | | 5 | Server-Side Rendering | HIGH | Hydration, prerendering | | 6 | Template Optimization | MEDIUM | Control flow, pipes | | 7 | State Management | MEDIUM | Signal patterns, selectors | | 8 | Memory Management | LOW-MEDIUM | Cleanup, subscriptions | --- ## 1. Change Detection (CRITICAL) ### Use OnPush Change Detection ``typescript // CORRECT - OnPush with Signals @Component({ changeDetection: ChangeDetectionStrategy.OnPush, template: <div>{{ count() }}</div>, }) export class CounterComponent { count = signal(0); } // WRONG - Default change detection @Component({ template: <div>{{ count }}</div>, // Checked every cycle }) export class CounterComponent { count = 0; } ` ### Prefer Signals Over Mutable Properties `typescript // CORRECT - Signals trigger precise updates @Component({ template: <h1>{{ title() }}</h1> <p>Count: {{ count() }}</p> , }) export class DashboardComponent { title = signal("Dashboard"); count = signal(0); } // WRONG - Mutable properties require zone.js checks @Component({ template: <h1>{{ title }}</h1> <p>Count: {{ count }}</p> , }) export class DashboardComponent { title = "Dashboard"; count = 0; } ` ### Enable Zoneless for New Projects `typescript // main.ts - Zoneless Angular (v20+) bootstrapApplication(AppComponent, { providers: [provideZonelessChangeDetection()], }); ` **Benefits:** - No zone.js patches on async APIs - Smaller bundle (~15KB savings) - Clean stack traces for debugging - Better micro-frontend compatibility --- ## 2. Async Operations & Waterfalls (CRITICAL) ### Eliminate Sequential Data Fetching `typescript // WRONG - Nested subscriptions create waterfalls this.route.params.subscribe((params) => { // 1. Wait for params this.userService.getUser(params.id).subscribe((user) => { // 2. Wait for user this.postsService.getPosts(user.id).subscribe((posts) => { // 3. Wait for posts }); }); }); // CORRECT - Parallel execution with forkJoin forkJoin({ user: this.userService.getUser(id), posts: this.postsService.getPosts(id), }).subscribe((data) => { // Fetched in parallel }); // CORRECT - Flatten dependent calls with switchMap this.route.params .pipe( map((p) => p.id), switchMap((id) => this.userService.getUser(id)), ) .subscribe(); ` ### Avoid Client-Side Waterfalls in SSR `typescript // CORRECT - Use resolvers or blocking hydration for critical data export const route: Route = { path: "profile/:id", resolve: { data: profileResolver }, // Fetched on server before navigation component: ProfileComponent, }; // WRONG - Component fetches data on init class ProfileComponent implements OnInit { ngOnInit() { // Starts ONLY after JS loads and component renders this.http.get("/api/profile").subscribe(); } } ` --- ## 3. Bundle Optimization (CRITICAL) ### Lazy Load Routes `typescript // CORRECT - Lazy load feature routes export const routes: Routes = [ { path: "admin", loadChildren: () => import("./admin/admin.routes").then((m) => m.ADMIN_ROUTES), }, { path: "dashboard", loadComponent: () => import("./dashboard/dashboard.component").then( (m) => m.DashboardComponent, ), }, ]; // WRONG - Eager loading everything import { AdminModule } from "./admin/admin.module"; export const routes: Routes = [ { path: "admin", component: AdminComponent }, // In main bundle ]; ` ### Use @defer for Heavy Components ``html <!-- CORRECT - Heavy component loads on demand --> @defer (on viewport) { <app-analytics-chart [data]="data