Blog

ngAfterContentChecked Example | Angular

Key Takeaways

  • ngAfterContentChecked runs after Angular checks a component's projected content, including the initial check after ngAfterContentInit.
  • A content check does not mean the projected content actually changed.
  • Keep this frequently executed hook small. Don't use it for one-time setup, routine requests, or expensive processing.
  • Read initialized content queries in ngAfterContentInit; prefer reactive queries or input-driven logic for ongoing updates.

Angular ngAfterContentChecked is a lifecycle hook that runs after Angular checks the content passed into a component. Use it only when you genuinely need to observe that post-check phase—not as a general setup or update mechanism.

Projected Content vs. the Component's View

Content projection lets a parent supply markup that a receiving component displays through an <ng-content> placeholder. That supplied markup is the receiving component's content; the elements declared in the receiving component's own template are its view.

For example, a panel's wrapping <section> belongs to its view, while a label supplied between <app-content-panel> tags is projected content. Angular's content projection guide explains this ownership distinction.

When Angular Calls ngAfterContentChecked

The relevant lifecycle order for one component is shown below. The initialization hooks run once; the checking hooks recur when Angular performs the corresponding checks.

Initial check:
ngOnInit
  -> ngDoCheck
  -> ngAfterContentInit
  -> ngAfterContentChecked
  -> ngAfterViewInit
  -> ngAfterViewChecked

Subsequent checks:
ngDoCheck
  -> ngAfterContentChecked
  -> ngAfterViewChecked

ngAfterContentInit runs after projected content is initialized. ngAfterContentChecked follows that initial content-check phase and runs again after subsequent content checks. ngAfterViewChecked concerns the component's own view, not its projected content. See Angular's lifecycle ordering documentation for the broader sequence.

For more detail on the surrounding hooks, read the ngDoCheck example and ngAfterViewChecked example.

The method signature is ngAfterContentChecked(): void. Conventionally, you declare implements AfterContentChecked to let TypeScript check the method name and signature; the interface is optional, and Angular calls the matching lifecycle method when present.

ngAfterContentChecked Example with ng-content

This example projects a changing label into a panel. A content query retrieves the directive attached to that label, so the panel can inspect its input without reading or modifying the DOM.

In a standalone Angular browser application whose index.html contains <app-root></app-root>, replace src/main.ts with the following. Both components explicitly use ChangeDetectionStrategy.Eager, which checks a component when the change-detection traversal reaches it, to make the repeated checks easy to observe.

import {
  AfterContentChecked,
  AfterContentInit,
  AfterViewChecked,
  ChangeDetectionStrategy,
  Component,
  Directive,
  DoCheck,
  OnInit,
  contentChild,
  input,
  signal,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

@Directive({
  selector: '[projectedLabel]',
  standalone: true,
})
export class ProjectedLabelDirective {
  readonly label = input.required<string>();
}

@Component({
  selector: 'app-content-panel',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.Eager,
  template: `
    <section>
      <h2>Panel view</h2>
      <ng-content></ng-content>
    </section>
  `,
})
export class ContentPanelComponent
  implements OnInit, DoCheck, AfterContentInit,
    AfterContentChecked, AfterViewChecked {
  readonly projectedLabel = contentChild(ProjectedLabelDirective);

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

  ngDoCheck(): void {
    console.log('ngDoCheck');
  }

  ngAfterContentInit(): void {
    console.log('ngAfterContentInit:', this.projectedLabel()?.label());
  }

  ngAfterContentChecked(): void {
    console.log('ngAfterContentChecked:', this.projectedLabel()?.label());
  }

  ngAfterViewChecked(): void {
    console.log('ngAfterViewChecked');
  }
}

@Component({
  selector: 'app-root',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.Eager,
  imports: [ContentPanelComponent, ProjectedLabelDirective],
  template: `
    <button type="button" (click)="changeLabel()">Change label</button>
    <button type="button" (click)="logOnly()">Log only</button>

    <app-content-panel>
      <span projectedLabel [label]="label()">{{ label() }}</span>
    </app-content-panel>
  `,
})
export class AppComponent {
  readonly label = signal('First label');

  changeLabel(): void {
    this.label.update(value =>
      value === 'First label' ? 'Updated label' : 'First label'
    );
  }

  logOnly(): void {
    console.log('Button clicked; label unchanged');
  }
}

bootstrapApplication(AppComponent).catch(error => {
  console.error('Application bootstrap failed:', error);
});

What to Observe

  1. Initial load: the panel logs ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked, and ngAfterViewChecked in that order. Both content hooks can read First label.
  2. Change label: the parent changes its signal, updating the projected text and directive input. The panel's next ngAfterContentChecked reads the updated label; ngAfterContentInit does not repeat.
  3. Log only: the handler changes neither the label nor the projected content, but Angular still processes the bound event and performs change detection. The content-check hook runs again with the same label.

The distinction matters: the event handler performs the action; the lifecycle hook observes a later checking phase. Don't treat hook executions as a count of meaningful content changes or assume one hook call per user action.

When Should You Use ngAfterContentChecked?

A reasonable use is temporary lifecycle instrumentation: inspect a projected directive's current value after its content check while diagnosing an integration problem. Production use should be exceptional. Angular explicitly warns that this hook runs frequently and can significantly affect performance.

Before implementing it, identify the actual trigger for your work:

What you needBetter starting point
One-time access to initialized projected childrenA content query read in ngAfterContentInit
Work caused by an input changengOnChanges or a derivation from signal inputs
A value derived from projected childrenA signal-based content query combined with computed
Resource cleanup when the component is destroyedngOnDestroy

Use Reactive Queries Instead of Copying State

For the example above, you can derive a display value rather than assigning it inside a checking hook:

// Fragment: add to ContentPanelComponent and add computed to its imports
// from '@angular/core'. The existing projectedLabel query stays in place.
// The component decorator and other existing members are omitted here.
class ContentPanelComponent {
  readonly projectedLabel = contentChild(ProjectedLabelDirective);

  readonly labelText = computed(
    () => this.projectedLabel()?.label() ?? 'No projected label'
  );
}

This reads both the query signal and the directive's input signal. It can therefore track label updates even when the same directive instance remains projected; it doesn't rely only on the query result's identity.

Query Availability Depends on the Query Type

Use ngAfterContentInit for one-time access to initialized content queries, as the example does. An optional contentChild query can return undefined when no matching child exists, so handle absence rather than assuming a child is always present.

For decorator-based @ContentChild queries, static: false is the default: the query resolves before ngAfterContentInit and updates as matching content changes. A static: true query resolves before ngOnInit but does not update afterward; use it only for content guaranteed to be present, not as a shortcut around lifecycle timing.

Common Mistakes

Changing Bound State During the Check

// Anti-pattern fragment: the component's template binds {{ checkedCount }}.
// The component decorator and other existing members are omitted here.
class ExampleComponent {
  checkedCount = 0;

  ngAfterContentChecked(): void {
    this.checkedCount += 1;
  }
}

Changing an already-checked binding can produce ExpressionChangedAfterItHasBeenCheckedError in development mode. Moving state updates to their actual cause—such as changeLabel() in the runnable example—avoids using a checking hook as an update loop. Angular's NG0100 explanation describes the consistency check behind this error.

Repeating Requests or Expensive Processing

A request, subscription, large collection scan, or repeated DOM measurement placed here may execute again on later checks even when the content hasn't meaningfully changed. Trigger requests from the relevant event or input change, establish subscriptions once, and keep cleanup tied to destruction. Don't force more change detection from this hook to compensate for state changes made inside it.

DOM Work Belongs After Rendering

ngAfterContentChecked is not a guarantee that the page's DOM is fully rendered. For necessary imperative work such as focusing an element or measuring geometry, Angular provides afterNextRender and afterEveryRender; register them in an injection context, typically a component constructor.

These render callbacks don't execute during server-side rendering or build-time prerendering. Prefer template bindings for ordinary DOM updates, and consult Angular's DOM API guidance before adding manual DOM work.

FAQ

When does Angular call ngAfterContentChecked?

Angular calls ngAfterContentChecked after checking a component's projected content. It can run even when the projected values haven't changed.

Does ngAfterContentChecked run on the first change-detection cycle?

Yes. It runs after ngAfterContentInit during the initial content-check phase, then after subsequent content checks.

What is the difference between ngAfterContentChecked and ngAfterViewChecked?

ngAfterContentChecked follows checks of content supplied to the component. ngAfterViewChecked follows checks of the component's own template and view children; neither hook means that values necessarily changed.

Can I change component state inside ngAfterContentChecked?

Don't use it for routine state updates. Changing an already-checked binding can cause ExpressionChangedAfterItHasBeenCheckedError in development mode, and scheduling additional updates from the hook can create repeated change-detection work.

What is a safe alternative to ngAfterContentChecked for one-time setup?

Use a content query with ngAfterContentInit when setup needs access to initially projected children. That hook runs once, but it is still not a safe place to mutate already-checked bindings.

Further Reading

Written by Sam Erickson

Software engineer. I write every article on StackChief myself. Author of Mastering the Tech Interview. More about me