Key Takeaways
ngAfterContentCheckedruns after Angular checks a component's projected content, including the initial check afterngAfterContentInit.- 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
-> ngAfterViewCheckedngAfterContentInit 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
- Initial load: the panel logs
ngOnInit,ngDoCheck,ngAfterContentInit,ngAfterContentChecked, andngAfterViewCheckedin that order. Both content hooks can readFirst label. - Change label: the parent changes its signal, updating the projected text and directive input. The panel's next
ngAfterContentCheckedreads the updated label;ngAfterContentInitdoes not repeat. - 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 need | Better starting point |
|---|---|
| One-time access to initialized projected children | A content query read in ngAfterContentInit |
| Work caused by an input change | ngOnChanges or a derivation from signal inputs |
| A value derived from projected children | A signal-based content query combined with computed |
| Resource cleanup when the component is destroyed | ngOnDestroy |
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
- AfterContentChecked API — the interface and callback contract.
- Angular component lifecycle — hook ordering, interfaces, and performance cautions.
- Content projection with ng-content — projected content and view ownership.
- Referencing children with queries — signal-based queries, computed values, and optional results.
- ContentChild API — static and dynamic query timing.
- ChangeDetectionStrategy API — the checking strategy used by the demonstration.
- Expression Changed After Checked — diagnosing changed bindings.
- Using DOM APIs — render callbacks and appropriate imperative DOM work.
