Angular lifecycle hooks are framework callbacks on components/directives that run at specific moments (creation, input changes, content/view init + checks, destruction). Interviewers care less about memorizing names and more about choosing the correct hook for inputs, DOM/ViewChild, content projection, change-detection edge cases, and cleanup. Framework focus: Angular templates + standalone components, @Input/@Output, and RxJS/async pipe patterns.
Use this Angular interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
Angular lifecycle hooks: when do they run, and what should you put in each?Frontend interview answer
This Angular interview question tests whether you can explain Angular lifecycle hooks run and what is each for, connect it to production trade-offs, and handle common follow-up questions.
- Angular lifecycle hooks run and what is each for explanation without falling back to memorized docs wording
- Lifecycle and Hooks reasoning, edge cases, and production failure modes
- How you would answer the most likely Angular interview follow-up
Core idea
Hooks are Angular-called methods that let you run code at predictable lifecycle points. The practical skill is picking the right hook:
• Inputs changed? ngOnChanges
• Initialize once? ngOnInit
• Projected content? ngAfterContent*
• DOM / ViewChild? ngAfterViewInit
• Cleanup? ngOnDestroy
Hook | When it runs | Frequency | What it’s for |
|---|---|---|---|
| Class instantiation (before Angular bindings) | Once | DI + minimal field setup. No inputs, no DOM. |
| After an | Many times (or zero if no inputs) | React to input changes; validate/normalize; derive state from inputs. |
| After first | Once | Initialization that depends on inputs/services; start streams; fetch data. |
| During every change detection run | Many times | Rare: custom change tracking when default checks aren’t enough (use carefully). |
| After projected content ( | Once | Work that needs |
| After every check of projected content | Many times | Rare: respond to projected content changes (avoid heavy work). |
| After component view + child views are created | Once | Safe DOM/ViewChild access; measure/focus; init 3rd-party widgets. |
| After every check of the component view | Many times | Rare: respond after view checks (avoid state writes here). |
| Right before Angular destroys the instance | Once | Cleanup: subscriptions, timers, listeners, 3rd-party teardown. |
Scenario | Correct hook | Why |
|---|---|---|
Initialize streams / fetch data |
| Runs once when bindings are ready. |
Derive state from |
| You get |
Access |
| View is guaranteed to exist (default |
Access projected content via |
| Projected content is initialized (different from the view). |
Tear down subscriptions, intervals, 3rd-party widgets |
| Last guaranteed cleanup point. |
Common task | Best hook | Why not the nearby hook? |
|---|---|---|
Kick off the first load that depends on bound inputs |
| Constructor is too early because Angular has not finished the first input-binding pass. |
React when the parent changes |
|
|
Read projected tabs via |
|
|
Measure a |
|
|
Tear down polling, listeners, and widget instances |
| Cleanup in init hooks leaks because Angular may destroy the component long after setup. |
import {
AfterContentChecked,
AfterContentInit,
AfterViewChecked,
AfterViewInit,
Component,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
ViewChild,
ElementRef,
} from '@angular/core';
@Component({
selector: 'app-life',
template: `
<p #p>Hello</p>
<ng-content></ng-content>
`
})
export class LifeComponent
implements
OnChanges,
OnInit,
AfterContentInit,
AfterContentChecked,
AfterViewInit,
AfterViewChecked,
OnDestroy {
@Input() value = 0;
@ViewChild('p') p?: ElementRef<HTMLParagraphElement>;
constructor() {
console.log('constructor');
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['value']) {
console.log('ngOnChanges', changes['value'].previousValue, '->', changes['value'].currentValue);
}
}
ngOnInit(): void {
console.log('ngOnInit');
}
ngAfterContentInit(): void {
console.log('ngAfterContentInit');
}
ngAfterContentChecked(): void {
console.log('ngAfterContentChecked');
}
ngAfterViewInit(): void {
console.log('ngAfterViewInit');
// Safe: ViewChild available (default static:false)
this.p?.nativeElement.focus?.();
}
ngAfterViewChecked(): void {
console.log('ngAfterViewChecked');
}
ngOnDestroy(): void {
console.log('ngOnDestroy');
}
}
Execution order (practical)
First render (typical):constructor → ngOnChanges? → ngOnInit → ngAfterContentInit → ngAfterContentChecked → ngAfterViewInit → ngAfterViewChecked
Later change-detection cycles:ngOnChanges? → ngDoCheck → ngAfterContentChecked → ngAfterViewChecked
Note: ngOnChanges only runs for @Input changes from the parent binding; it won’t fire just because you mutated an object in-place without changing the input reference.
@Component({
selector: 'app-tabs-shell',
template: `
<section #panel><ng-content></ng-content></section>
`
})
export class TabsShellComponent implements AfterContentInit, AfterViewInit {
@ContentChild(TabLabelDirective) projectedLabel?: TabLabelDirective;
@ViewChild('panel') panel?: ElementRef<HTMLElement>;
ngAfterContentInit(): void {
console.log('Projected label is ready:', this.projectedLabel?.text);
}
ngAfterViewInit(): void {
console.log('Own panel element is ready:', this.panel?.nativeElement.offsetWidth);
}
}
Newer Angular note: afterNextRender
If you only need a post-render callback and not a class lifecycle method, newer Angular APIs like afterNextRender can be a cleaner fit. It does not replace the meaning of ngOnChanges, ngAfterContentInit, or ngOnDestroy; it is just another timing tool for render-following work.
Pitfall (interview-grade) | What happens | Fix |
|---|---|---|
Mutating bindings in | Dev mode can throw | Avoid sync bound writes there; schedule (microtask) or refactor logic earlier. |
Forgetting cleanup | Intervals/subscriptions keep running after navigation | Use |
Heavy work in | Runs often → perf issues, jank | Keep these hooks rare and cheap; prefer pure derivations and OnPush patterns. |
Expecting | No call if input reference doesn’t change | Use immutable updates (new reference) or manual detection logic if needed. |
Confusing content vs view | Using | Use |
import { Component, DestroyRef, inject, OnInit } from '@angular/core';
import { interval } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-clean',
template: `...`
})
export class CleanComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef);
ngOnInit(): void {
// Auto-cleanup when the component is destroyed.
interval(1000)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(v => console.log(v));
}
}
Lifecycle hooks are Angular callbacks around creation, binding updates, view/content init + checks, and teardown. For interviews: know ngOnChanges (inputs), ngOnInit (init once), ngAfterViewInit (DOM/ViewChild), and ngOnDestroy (cleanup), plus the content-vs-view distinction and the common pitfalls.
Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.