TL;DR — Angular 20’s zoneless runtime means you can think about lifecycle hooks as pure, predictable events. Hooks like
ngOnInit,ngOnDestroy, and the newafterNextRenderfit naturally into the signal‑based, reactive model of modern Angular. Let’s explore every stage — from creation to destruction — using real code you can paste and watch come to life.
Introduction: Lifecycle Reimagined in Angular 20
Angular’s component lifecycle defines the sequence of steps from creation to destruction. Each hook represents a checkpoint in rendering and change detection.
In Angular 20, lifecycle hooks are more predictable, signal-friendly, and zone‑less compatible. Combined with new rendering callbacks (afterNextRender, afterEveryRender) you gain granular control without NgZone.
The Big Picture — Lifecycle Flow
| Phase | Hook | Summary |
|---|---|---|
| Creation | constructor() |
Initializes the component class. Avoid side effects. |
| Change Detection | ngOnInit() |
Runs once after inputs initialize. |
ngOnChanges() |
Runs every time inputs change. | |
ngDoCheck() |
Custom change detection logic (use sparingly). | |
| Content Projection | ngAfterContentInit() |
Once after projected content initializes. |
ngAfterContentChecked() |
After each check of projected content. | |
| View Initialization | ngAfterViewInit() |
Once after the view initializes. |
ngAfterViewChecked() |
After each check of the view. | |
| Rendering | afterNextRender() |
Once after all components render. |
afterEveryRender() |
Runs every render cycle. | |
| Destruction | ngOnDestroy() |
Cleanup before component removal. |
Hands‑on Example — Lifecycle Hooks in Action
Here’s a full working component demonstrating every lifecycle phase, including Signals and Zoneless rendering callbacks:
import {
afterNextRender,
Component,
effect,
OnChanges,
OnInit,
signal,
} from '@angular/core';
import { TitleComponent } from '../../components/title/title.component';
const log = (...messages: string[]) => {
console.log(`%c${messages.join(' ')}`, 'color: #4ef2af; font-weight: bold');
};
@Component({
selector: 'app-home-page',
imports: [TitleComponent],
templateUrl: './home-page.component.html',
})
export class HomePageComponent implements OnInit, OnChanges {
traditionalProperty = 'Cristian';
signalProperty = signal('Cristian');
constructor() {
log('constructor()', 'Called first — minimal setup only');
}
changeTraditional() {
this.traditionalProperty = 'Cristian Sifuentes';
}
changeSignal() {
this.signalProperty.set('Cristian Sifuentes');
}
basicEffect = effect((onCleanup) => {
log('effect()', 'Signal side effect triggered');
onCleanup(() => log('cleanup()', 'Runs when effect is destroyed'));
});
ngOnInit() {
log('ngOnInit()', 'After inputs initialized — setup subscriptions or fetch data');
}
ngOnChanges() {
log('ngOnChanges()', 'Runs when @Input() properties change');
}
ngDoCheck() {
log('ngDoCheck()', 'Manual change detection run');
}
ngAfterContentInit() {
log('ngAfterContentInit()', 'Projected content initialized');
}
ngAfterContentChecked() {
log('ngAfterContentChecked()', 'Projected content checked');
}
ngAfterViewInit() {
log('ngAfterViewInit()', 'View and children initialized');
}
ngAfterViewChecked() {
log('ngAfterViewChecked()', 'View checked for changes');
}
ngOnDestroy() {
log('ngOnDestroy()', 'Cleanup before destruction — unsubscribe, detach listeners');
}
afterNextRenderEffect = afterNextRender(() => {
log('afterNextRender()', 'All components rendered to DOM');
});
}
Understanding New Hooks: afterNextRender() & afterEveryRender()
These are application‑level render callbacks, not per‑component hooks. They execute once all DOM updates have completed.
Example with phases:
import { Component, ElementRef, afterNextRender, inject } from '@angular/core';
@Component({
selector: 'app-profile',
template: `<div>User Profile</div>`
})
export class ProfileComponent {
private el = inject(ElementRef);
constructor() {
afterNextRender({
write: () => {
const el = this.el.nativeElement;
el.style.opacity = '1';
return el.getBoundingClientRect().height;
},
read: (height) => {
console.log('Final element height:', height);
},
});
}
}
Phases
| Phase | Description |
|---|---|
earlyRead |
Read layout-affecting properties before any writes. |
mixedReadWrite |
Default phase; both reads and writes allowed. |
write |
Apply layout changes (DOM writes). |
read |
Read layout data after writes (e.g., bounding boxes). |
Use phases to avoid layout thrashing and optimize performance in zoneless mode.
Zoneless Best Practices (Angular 20+)
- Prefer Signals and Effects to trigger updates instead of relying on Zones.
- Use
afterNextRenderto coordinate DOM updates safely. - Avoid calling
detectChanges()manually — rely on the reactive model. - Cleanup logic goes in
ngOnDestroyor viaDestroyRef. - Remember: in zoneless Angular, your updates are explicit — this improves performance and predictability.
Cleanup with DestroyRef
import { Component, DestroyRef, inject } from '@angular/core';
@Component({
selector: 'app-user-profile',
template: `User profile works!`,
})
export class UserProfile {
private destroyRef = inject(DestroyRef);
constructor() {
this.destroyRef.onDestroy(() => console.log('Destroyed!'));
}
}
Use DestroyRef to keep setup and teardown close together — ideal for reactive, self-contained services.
Execution Order Overview
During Initialization
constructor()
ngOnChanges()
ngOnInit()
ngDoCheck()
ngAfterContentInit()
ngAfterViewInit()
ngAfterContentChecked()
ngAfterViewChecked()
afterNextRender()
afterEveryRender()
During Updates
ngOnChanges()
ngDoCheck()
ngAfterContentChecked()
ngAfterViewChecked()
afterEveryRender()
Expert Tips & Pitfalls
✅ Use ngOnInit for async initialization — ideal for signals, HTTP fetches, and event listeners.
✅ Avoid state changes in ngAfter*Checked — they will trigger ExpressionChanged errors.
✅ Leverage afterNextRender for animations or measurement post-DOM update.
✅ DestroyRef > ngOnDestroy when collaborating with external logic.
✅ Prefer signals and computed properties for reactive, lightweight patterns.
Resources
🧠 Angular 20 is about precision — clean lifecycles, explicit renders, and reactive purity. Master these hooks, and you’ll master change detection itself.*

Top comments (1)
Such a clean breakdown of Angular 20’s zoneless lifecycle — especially the new render callbacks and DestroyRef pattern. The framework feels sharper and more explicit than ever. Great work! ⚡