Component Lifecycle Hooks

Understand the stages of an Angular component and use lifecycle hooks for initialization, updates, view access, and cleanup.

Lesson content

What are Component Lifecycle Hooks? Component lifecycle hooks are methods that Angular calls automatically during different stages of a component's life. A component is created, initialized, checked, rendered, updated, and finally destroyed. Lifecycle Order constructor() ↓ ngOnChanges() ↓ ngOnInit() ↓ ngDoCheck() ↓ ngAfterContentInit() ↓ ngAfterContentChecked() ↓ ngAfterViewInit() ↓ ngAfterViewChecked() ↓ afterNextRender() / afterEveryRender() ↓ ngOnDestroy() Common Lifecycle Hooks Hook When it Runs ngOnChanges() Runs when component inputs change ngOnInit() Runs once after inputs are initialized ngDoCheck() Runs whenever Angular checks the component ngAfterContentInit() Runs once after projected content initializes ngAfterContentChecked() Runs after projected content is checked ngAfterViewInit() Runs once after the component view initializes ngAfterViewChecked() Runs after the component view is checked ngOnDestroy() Runs once before the component is destroyed Lifecycle Example import { Component, Input, OnChanges, OnInit, OnDestroy, SimpleChanges } from '@angular/core'; @Component({ selector: 'app-user', standalone: true, template: '<h2>{{ name }}</h2>' }) export class UserComponent implements OnChanges, OnInit, OnDestroy { @Input() name = ''; ngOnChanges(changes: SimpleChanges) { console.log('Input changed', changes); } ngOnInit() { console.log('Component initialized'); } ngOnDestroy() { console.log('Component destroyed'); } } ngOnInit() Use ngOnInit() for component initialization after inputs are ready. ngOnInit() { this.loadUsers(); } ngOnChanges() Use ngOnChanges() when the component must respond to input changes. @Input() userId = 0; ngOnChanges(changes: SimpleChanges) { if (changes['userId']) { this.loadUser( changes['userId'].currentValue ); } } ngAfterViewInit() Use ngAfterViewInit() when the component view or child view is required. @ViewChild('searchInput') searchInput!: ElementRef<HTMLInputElement>; ngAfterViewInit() { this.searchInput.nativeElement.focus(); } ngOnDestroy() Use ngOnDestroy() to clean timers, subscriptions, event listeners, or WebSocket connections. private timer?: number; ngOnInit() { this.timer = window.setInterval(() => { console.log('Running'); }, 1000); } ngOnDestroy() { window.clearInterval(this.timer); } Modern Angular Render Lifecycle Modern Angular also provides render callback functions that run after Angular finishes rendering all components into the DOM. afterNextRender() – Runs once after the next complete render afterEveryRender() – Runs after every complete render afterRenderEffect() – Runs reactively after rendering when tracked signals change Unlike traditional lifecycle hooks, these are standalone functions rather than component class methods. They are normally registered inside an injection context such as a component constructor. afterNextRender() and afterEveryRender() are application-wide render callbacks and do not run during server-side rendering or build-time prerendering. :contentReference[oaicite:0]{index=0} afterNextRender() import { afterNextRender, Component, ElementRef, viewChild } from '@angular/core'; @Component({ selector: 'app-search', standalone: true, template: '<input #searchInput />' }) export class SearchComponent { searchInput = viewChild.required<ElementRef<HTMLInputElement>>( 'searchInput' ); constructor() { afterNextRender({ write: () => { this.searchInput() .nativeElement .focus(); } }); } } afterEveryRender() import { afterEveryRender, Component } from '@angular/core'; @Component({ selector: 'app-dashboard', standalone: true, template: '<h2>Dashboard</h2>' }) export class DashboardComponent { constructor() { afterEveryRender({ read: () => { console.log('Rendering completed'); } }); } } afterRenderEffect() Use afterRenderEffect() for reactive DOM work that depends on signals. import { afterRenderEffect, Component, ElementRef, input, viewChild } from '@angular/core'; @Component({ selector: 'app-chart', standalone: true, template: '<canvas #canvas></canvas>' }) export class ChartComponent { data = input.required<number[]>(); canvas = viewChild.required<ElementRef<HTMLCanvasElement>>( 'canvas' ); constructor() { afterRenderEffect({ write: () => { updateChart( this.canvas().nativeElement, this.data() ); } }); } } afterRenderEffect() runs on browser platforms after rendering and reruns when its signal dependencies become dirty. Angular recommends choosing explicit phases such as write and read instead of mixing DOM reads and writes. :contentReference[oaicite:1]{index=1} Render Phases Phase Purpose earlyRead Read DOM values before writing when necessary write Write styles, properties, or DOM values mixedReadWrite Read and write together; avoid when possible read Read DOM layout after writes complete Best Practices Use ngOnInit() for initialization Use ngOnChanges() for input changes Use ngAfterViewInit() only when view access is required Use ngOnDestroy() for manual cleanup Prefer takeUntilDestroyed() or the async pipe for subscriptions Avoid heavy logic inside frequently called checking hooks Use render callbacks for manual DOM operations and third-party UI libraries Prefer separate write and read phases Key Point Lifecycle hooks control component initialization, change detection, rendering, and cleanup. Easy Formula Create ↓ Initialize ↓ Check ↓ Render ↓ Update ↓ Destroy