Back to articles

Angular @ViewChild

10 min
Front-endAngular

@ViewChild gives you a direct reference to a DOM element or child component from within your TypeScript code.

It's the go-to solution when you need to manipulate the DOM directly or call a method on a child component.


Accessing a DOM Element

Declare a template variable with # in the template, then use @ViewChild to grab a reference to it in TypeScript.

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

@Component({
  standalone: true,
  selector: 'app-demo',
  template: `
    
    
  `,
})
export class DemoComponent implements AfterViewInit {
  @ViewChild('nameInput') nameInput!: ElementRef;

  ngAfterViewInit() {
    console.log(this.nameInput.nativeElement);
  }

  focusInput() {
    this.nameInput.nativeElement.focus();
  }
}

@ViewChild('nameInput') takes the template variable name as an argument and returns an ElementRef. Access the underlying DOM node through ElementRef.nativeElement.


Accessing a Child Component

@ViewChild also works with child components, giving you access to their properties and methods directly.

Child component

TypeScript
import { Component } from '@angular/core';

@Component({
  standalone: true,
  selector: 'app-timer',
  template: `

{{ seconds }}s

`, }) export class TimerComponent { seconds = 0; start() { setInterval(() => { this.seconds++; }, 1000); } reset() { this.seconds = 0; } }

Parent component

TypeScript
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { TimerComponent } from './timer.component';

@Component({
  standalone: true,
  imports: [TimerComponent],
  selector: 'app-parent',
  template: `
    
    
    
  `,
})
export class ParentComponent implements AfterViewInit {
  @ViewChild(TimerComponent) timer!: TimerComponent;

  ngAfterViewInit() {
    console.log(this.timer); // TimerComponent instance
  }

  startTimer() {
    this.timer.start();
  }

  resetTimer() {
    this.timer.reset();
  }
}

Pass the class name instead of a string to get a typed reference to the component instance.


@ViewChildren

When the template has multiple elements or components of the same type, use @ViewChildren to get all of them as a QueryList:

TypeScript
import { Component, ViewChildren, QueryList, ElementRef, AfterViewInit } from '@angular/core';

@Component({
  standalone: true,
  selector: 'app-demo',
  template: `
    
    
    
    
  `,
})
export class DemoComponent implements AfterViewInit {
  @ViewChildren('item') items!: QueryList>;

  ngAfterViewInit() {
    console.log(this.items.length); // 3
  }

  focusAll() {
    this.items.forEach(item => item.nativeElement.focus());
  }
}

QueryList supports forEach, map, filter, and more. You can also subscribe to changes to react when the list updates:

TypeScript
ngAfterViewInit() {
  this.items.changes.subscribe(() => {
    console.log('list changed');
  });
}

Things to Know

Only available after ngAfterViewInit

@ViewChild references aren't populated until after the view has been initialized. Accessing them in ngOnInit returns undefined:

TypeScript
ngOnInit() {
  console.log(this.nameInput); // undefined
}

ngAfterViewInit() {
  console.log(this.nameInput); // ElementRef — ready to use
}

The static Option

@ViewChild has a static option that defaults to false:

  • static: false (default): resolved after change detection; available in ngAfterViewInit
  • static: true: resolved before change detection; available in ngOnInit, but only works for elements that aren't inside *ngIf or *ngFor
TypeScript
@ViewChild('nameInput', { static: true }) nameInput!: ElementRef;

ngOnInit() {
  console.log(this.nameInput); // works because static: true
}

Don't Overuse It

Direct DOM manipulation bypasses Angular's change detection and can lead to hard-to-track bugs. Reach for data binding first; use @ViewChild only when you genuinely need direct DOM access.


viewChild() (Angular 17+)

Angular 17 introduced viewChild(), a Signal-based alternative to @ViewChild:

TypeScript
import { Component, viewChild, ElementRef } from '@angular/core';

@Component({
  standalone: true,
  selector: 'app-demo',
  template: ``,
})
export class DemoComponent {
  nameInput = viewChild('nameInput');
  // required version:
  nameInputRequired = viewChild.required('nameInput');
}

viewChild() returns a Signal, so you read the value by calling it with ():

TypeScript
ngAfterViewInit() {
  console.log(this.nameInput()?.nativeElement);
  console.log(this.nameInputRequired().nativeElement); // no ? needed
}

@ViewChild vs. viewChild()

@ViewChildviewChild()
StyleDecoratorFunction
Return typeDirect valueSignal
Reading valueDirect property accessCall with ()
Required! assertionviewChild.required()
Angular versionAll versionsv17+

Summary

  • @ViewChild: gets a reference to a single DOM element or child component
  • @ViewChildren: gets a QueryList of all matching elements or components
  • References aren't available until ngAfterViewInit
  • Angular 17+ offers viewChild() as a Signal-based alternative, the recommended approach for new projects