Interview answer drill

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

HighIntermediateAngular
Interview focus

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
Practice more Angular interview questions
Interview quick answer

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.

Full interview answer

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

constructor

Class instantiation (before Angular bindings)

Once

DI + minimal field setup. No inputs, no DOM.

ngOnChanges(changes)

After an @Input binding changes

Many times (or zero if no inputs)

React to input changes; validate/normalize; derive state from inputs.

ngOnInit()

After first ngOnChanges (or after first binding setup)

Once

Initialization that depends on inputs/services; start streams; fetch data.

ngDoCheck()

During every change detection run

Many times

Rare: custom change tracking when default checks aren’t enough (use carefully).

ngAfterContentInit()

After projected content (<ng-content>) is set

Once

Work that needs @ContentChild/@ContentChildren to exist.

ngAfterContentChecked()

After every check of projected content

Many times

Rare: respond to projected content changes (avoid heavy work).

ngAfterViewInit()

After component view + child views are created

Once

Safe DOM/ViewChild access; measure/focus; init 3rd-party widgets.

ngAfterViewChecked()

After every check of the component view

Many times

Rare: respond after view checks (avoid state writes here).

ngOnDestroy()

Right before Angular destroys the instance

Once

Cleanup: subscriptions, timers, listeners, 3rd-party teardown.

Lifecycle hooks, timing, and intent

Scenario

Correct hook

Why

Initialize streams / fetch data

ngOnInit

Runs once when bindings are ready.

Derive state from @Input() changes

ngOnChanges

You get SimpleChanges with prev/current values.

Access @ViewChild / DOM measurements / focus

ngAfterViewInit

View is guaranteed to exist (default static: false).

Access projected content via @ContentChild

ngAfterContentInit

Projected content is initialized (different from the view).

Tear down subscriptions, intervals, 3rd-party widgets

ngOnDestroy

Last guaranteed cleanup point.

Picking the right hook (what interviewers actually test)

Common task

Best hook

Why not the nearby hook?

Kick off the first load that depends on bound inputs

ngOnInit

Constructor is too early because Angular has not finished the first input-binding pass.

React when the parent changes @Input() filters later

ngOnChanges

ngOnInit only runs once; it will not react to later parent updates.

Read projected tabs via @ContentChild

ngAfterContentInit

ngAfterViewInit is about the component view, not projected content from the parent.

Measure a @ViewChild input and focus it

ngAfterViewInit

ngOnInit is usually too early for view queries with the default static: false timing.

Tear down polling, listeners, and widget instances

ngOnDestroy

Cleanup in init hooks leaks because Angular may destroy the component long after setup.

Scenario matrix interviewers actually expect next
TYPESCRIPT
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.

TYPESCRIPT
@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 ngAfterViewInit/ngAfterViewChecked

Dev mode can throw ExpressionChangedAfterItHasBeenCheckedError

Avoid sync bound writes there; schedule (microtask) or refactor logic earlier.

Forgetting cleanup

Intervals/subscriptions keep running after navigation

Use ngOnDestroy, async pipe, or takeUntilDestroyed.

Heavy work in ngAfterViewChecked/ngDoCheck

Runs often → perf issues, jank

Keep these hooks rare and cheap; prefer pure derivations and OnPush patterns.

Expecting ngOnChanges on internal mutations

No call if input reference doesn’t change

Use immutable updates (new reference) or manual detection logic if needed.

Confusing content vs view

Using @ViewChild when you needed @ContentChild (or vice versa)

Use AfterView* for view; AfterContent* for projection.

Common lifecycle bugs seniors mention
TYPESCRIPT
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));
  }
}
                  
Summary

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.

Similar questions
Guides
Preparing for interviews?

Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.