Interview answer drill

Use this Angular interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.

How does *ngIf affect the DOM and component lifecycle?Frontend interview answer

HighIntermediateAngular
Interview focus

This Angular interview question tests whether you can explain Angular *ngIf in production: DOM teardown, lifecycle resets, and hide vs destroy, connect it to production trade-offs, and handle common follow-up questions.

  • Angular *ngIf in production: DOM teardown, lifecycle resets, and hide vs destroy explanation without falling back to memorized docs wording
  • Directives and Ngif 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

*ngIf is not a visibility toggle; it creates and destroys embedded views. The practical angle is lifecycle and debug behavior: state resets, ngOnDestroy cleanup, and the production choice between real teardown and simply hiding UI.

Full interview answer

Debug mental model

*ngIf does not hide a subtree; it creates and destroys an embedded view. In production, that difference shows up as reset form state, fresh child instances, repeated init work, and cleanup bugs when subscriptions or listeners are tied to that subtree.

HTML
<!-- Shorthand -->
<app-child *ngIf="show"></app-child>

<!-- Roughly equivalent to -->
<ng-template [ngIf]="show">
  <app-child></app-child>
</ng-template>
                  

When condition flips

DOM result

Instance result

Lifecycle impact

false → true

Nodes are inserted into the DOM

New component/directive instances are created

constructor → (ngOnChanges) → ngOnInit → view/content hooks

true → false

Nodes are removed from the DOM

Instances are destroyed

ngOnDestroy runs; subscriptions/cleanup should happen here

true → true (condition stays true)

DOM stays

Same instances stay

Normal change detection updates bindings

What *ngIf actually changes
TYPESCRIPT
// child.component.ts
import { Component, OnDestroy, OnInit } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `Child is alive`
})
export class ChildComponent implements OnInit, OnDestroy {
  constructor() {
    console.log('child: constructor');
  }

  ngOnInit(): void {
    console.log('child: ngOnInit');
  }

  ngOnDestroy(): void {
    console.log('child: ngOnDestroy');
  }
}

// parent template
// <button (click)="show = !show">toggle</button>
// <app-child *ngIf="show"></app-child>
                  

Topic

*ngIf behavior

Practical consequence

Component state

Reset on every re-create

Local fields, form state, and child component state are lost on hide/show

Subscriptions/listeners

Destroyed view stops emitting into template

If you subscribe manually, you must unsubscribe in ngOnDestroy (or use async pipe / takeUntilDestroyed)

DI scope

Component-level providers are torn down with the view

A service provided in the component gets a fresh instance on next show

View queries

ViewChild/ContentChild inside the block disappear

References become undefined when view is destroyed; guard access

Change detection cost

Removed subtree is not checked

Hiding heavy UI with *ngIf can reduce CD + DOM updates

Key implications interviewers care about

Technique

Does it remove from DOM?

Does it destroy component instances?

When to prefer

*ngIf

Yes

Yes

When you want real teardown (free resources, remove listeners, skip CD)

[hidden] / CSS (display:none)

No

No

When you need to preserve state and avoid re-creation cost

ng-container + else template

Same as *ngIf

Same as *ngIf

When you want clean conditional branching without extra wrapper DOM

*ngIf vs hiding

Common gotcha

What happens

Fix

Toggling a form with *ngIf

User input resets on every hide/show

Use [hidden] if you must preserve state, or persist the form model outside the child

Expensive init in ngOnInit

Runs every time the view is recreated

Cache in a service/facade, or keep component alive and just hide it

Memory leaks

Manual subscriptions keep running if not cleaned up

Use async pipe, takeUntilDestroyed(), or ngOnDestroy cleanup

Practical pitfalls
HTML
<!-- destroy + recreate every time -->
<app-profile-form *ngIf="showForm"></app-profile-form>

<!-- keep the same child instance alive -->
<app-profile-form [hidden]="!showForm"></app-profile-form>

<!-- If the child keeps local draft text or component-scoped providers,
     the *ngIf version recreates them on every show -->
                  

Worked toggle scenario

If a child component keeps local draft text, a pending timer, or a component-scoped service, flipping *ngIf false tears all of that down. Flipping it true creates a fresh child, reruns init hooks, and starts new subscriptions or async-pipe subscriptions inside that subtree. Use hiding when the requirement is "keep the current instance and its local state alive"; use *ngIf when the requirement is real teardown.

Interview pivots after *ngIf

Once you explain that *ngIf creates and destroys embedded views, interviewers often branch into nearby Angular topics. The clean way to answer them is to keep the same lens: what survives teardown, what gets recreated, and which concern is actually separate from conditional view existence.

Follow-up

Short answer

Go deeper

Memory leaks

Because *ngIf destroys the subtree, long-lived manual subscriptions, timers, or global listeners must be cleaned up on teardown. async pipe and takeUntilDestroyed() are the usual safer defaults.

Angular memory leaks: /angular/trivia/angular-prevent-memory-leaks-unsubscribe-patterns

Change detection

A subtree removed by *ngIf is not checked at all, which is one reason real teardown can reduce work. That is different from keeping the subtree alive and merely hiding it.

Angular change detection strategies: /angular/trivia/angular-change-detection-strategies

Constructor vs ngOnInit

When *ngIf recreates a child, Angular runs the normal creation path again: constructor, initial input binding, and init hooks. That is why repeated toggles rerun setup code.

Angular constructor vs ngOnInit: /angular/trivia/angular-ngoninit-vs-constructor

Routing

Routing swaps routed views based on the URL; *ngIf only toggles a template branch inside the current view. They both affect what is visible, but they operate at different layers.

Angular routing: /angular/trivia/angular-routing

Two-way binding

Two-way binding solves value synchronization, not instance preservation. A control under *ngIf can still lose local state even if its value is bound both ways.

Angular custom two-way binding: /angular/trivia/angular-custom-two-way-binding

Structural directives

*ngIf is the concrete structural-directive example: it rewrites onto an <ng-template> and creates or clears an embedded view. That is why this is a DOM-shape question, not a simple visibility question.

Structural vs attribute directives: /angular/trivia/angular-structural-vs-attribute-directives

Performance

Destroying a heavy branch with *ngIf can cut DOM and change-detection cost, but frequent re-creation can also rerun expensive init work. The correct answer depends on whether the bottleneck is idle checking or repeated setup.

Angular performance optimization: /angular/trivia/angular-performance-optimization

Dependency injection

If a provider lives at the component level, *ngIf teardown destroys that injector scope too. Showing the branch again creates a fresh component instance and a fresh provider instance.

Angular dependency injection: /angular/trivia/angular-dependency-injection

Angular services

This is the practical boundary interviewers care about: local component state disappears with *ngIf, while shared logic or cached state kept in a longer-lived service can survive. Services and component-local state do not have the same lifetime.

Angular services: /angular/trivia/angular-services

NgRx

Store state survives because it lives outside the torn-down child view. What resets under *ngIf is the local component instance, not the global store.

NgRx data flow: /angular/trivia/ngrx-data-flow-end-to-end-angular

Lazy loading

Lazy loading decides when Angular fetches code for a route or feature boundary; *ngIf decides whether an already-loaded view branch exists right now. One is bundle-loading strategy, the other is conditional rendering.

Angular lazy loading: /angular/trivia/angular-lazy-loading

Angular vs AngularJS

Modern Angular *ngIf is best explained as embedded-view creation and destruction, not as an AngularJS-style digest or show-hide mental model. That difference matters when discussing lifecycle hooks and instance teardown.

Angular vs AngularJS: /angular/trivia/angular-vs-angularjs

Custom directives

If the discussion turns into "how would you build this yourself?", the senior answer is TemplateRef plus ViewContainerRef managing an embedded view. That is the same mechanism *ngIf sits on top of.

Angular directives: /angular/trivia/angular-directives

Related follow-ups interviewers often branch into after *ngIf
Summary

*ngIf creates/destroys an embedded view. True = build DOM + instantiate components/directives and run init hooks. False = remove DOM + destroy instances and run ngOnDestroy. Use it when you want real teardown and to skip change detection for that subtree; use hiding when you need to keep state alive.

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.