What’s the difference between constructor, ngOnInit, and ngAfterViewInit, and which one is correct for DOM/ViewChild access?

LowIntermediateAngular
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

constructor is for DI + trivial class setup, ngOnInit is for initialization that depends on inputs/bindings, and ngAfterViewInit is for work that needs the component view (DOM, ViewChild). For DOM/ViewChild access, ngAfterViewInit is usually correct—except @ViewChild({ static: true }) cases which are available earlier.

Answer

Core idea

These run at different moments in the component lifecycle. The right hook depends on what you need: DI only (constructor), inputs/bindings (ngOnInit), or the rendered view / ViewChild / DOM (ngAfterViewInit).

Hook

When it runs

What it’s for

DOM / ViewChild availability

constructor

When the class instance is created (before Angular initializes bindings).

Dependency injection + minimal field initialization (no Angular-dependent logic).

❌ Not safe for DOM. View is not created. ViewChild not resolved.

ngOnInit()

Once, after Angular sets data-bound inputs (after first ngOnChanges).

Initialization that depends on inputs, services, starting streams, fetching data.

⚠️ Usually not for DOM. ViewChild only if @ViewChild({ static: true }).

ngAfterViewInit()

Once, after Angular finishes creating the component’s view + child views.

Work that needs the rendered view: @ViewChild, DOM measurements, focus, 3rd-party widgets init.

✅ Safe for DOM/ViewChild (default static: false).

Constructor vs ngOnInit vs ngAfterViewInit

Which one is correct for DOM / ViewChild?

Use ngAfterViewInit() for DOM/ViewChild access in most cases. That’s when Angular guarantees the view exists and view queries (default static: false) are resolved.

Important nuance: @ViewChild static option

@ViewChild(..., { static: true }) resolves earlier (before/at init), so it can be used in ngOnInit() if the element is always present (not inside *ngIf/*ngFor). Default is static: false, which is available in ngAfterViewInit().

TYPESCRIPT
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';

@Component({
  selector: 'app-demo',
  template: `
    <input #nameInput />
    <!-- If this input were under *ngIf, static:true would be wrong -->
  `
})
export class DemoComponent implements OnInit, AfterViewInit {
  // Case A (common): default static:false => available in ngAfterViewInit
  @ViewChild('nameInput') nameInput?: ElementRef<HTMLInputElement>;

  // Case B: static:true => available in ngOnInit (ONLY if always present)
  // @ViewChild('nameInput', { static: true }) nameInput!: ElementRef<HTMLInputElement>;

  constructor(/* inject services here */) {
    // DI only. No DOM.
  }

  ngOnInit(): void {
    // Good: init that depends on @Input() or services.
    // If using static:true, you *can* access nameInput here.
  }

  ngAfterViewInit(): void {
    // Safe place for DOM/ViewChild
    this.nameInput?.nativeElement.focus();
  }
}
                  

Scenario

Correct hook

Why

Read @Input() values, set up streams, start API calls

ngOnInit

Inputs are set; component is initialized.

Access @ViewChild / measure DOM / focus element

ngAfterViewInit

View is created; queries are resolved.

ViewChild element is always present (not under *ngIf/*ngFor) and you need it early

ngOnInit + @ViewChild({ static: true })

Static query resolves before init.

Access projected content via @ContentChild

ngAfterContentInit

Projected content is initialized (different from the view).

Picking the right lifecycle hook

Common pitfall

If you change a bound value inside ngAfterViewInit, you can trigger ExpressionChangedAfterItHasBeenCheckedError in dev mode. Fix by avoiding sync bound mutations there, or scheduling them (microtask) / triggering a follow-up change detection deliberately (advanced).

Summary

constructor = DI + minimal setup. ngOnInit = initialization that depends on inputs/bindings. ngAfterViewInit = anything that needs the rendered view (DOM, ViewChild). For DOM/ViewChild access, ngAfterViewInit is the default correct choice, with the exception of @ViewChild({ static: true }) when the element is guaranteed to exist.

Similar questions
Guides
36 / 37