He who thinks change detection is depth-first and he who thinks it’s breadth-first are both usually right
Do you know if Angular first checks siblings of the current component (breadth-first) or its children (depth-first)? This article demonstrates that the answer depends on what operations of change detection you consider.

He who thinks change detection is depth-first and he who thinks it’s breadth-first are both usually right
Do you know if Angular first checks siblings of the current component (breadth-first) or its children (depth-first)? This article demonstrates that the answer depends on what operations of change detection you consider.


I was once asked if change detection in Angular is depth or breadth first. This basically means whether Angular first checks siblings of the current component (breadth-first) or its children (depth-first). I hadn’t given any prior thought to this question so I just went with my gut and the knowledge of internals. I declared that it was depth-first. Later, to check my assertion, I created a tree of components and put some logging logic inside the ngDoCheck
hook:
<>Copy@Component({ selector: 'r-comp', template: `{{addRender()}}` }) export class RComponent { ngDoCheck() { // holds all calls in order and is logged to console calls.ngDoCheck.push('R'); }
And to my surprise, it turned out that some siblings were checked first as depicted on the diagram below:


So here you see that Angular checks K
and then V
, L
and then C
and so on. So was I wrong and it’s really a breadth-first algorithm? Well, not exactly. First thing to notice in the above representation is that it’s not a proper breadth-first algorithm. The conventional implementation of the algorithm checks all siblings on the same level, whereas in the diagram above as you can see the algorithm indeed checks L
and C
sibling components, but instead of checking X
and F
it goes down to J
and O
. Also, the implementation of the breadth-first graph traversal algorithm is well defined but I couldn’t find it in the sources. So I decided to run another experiment and put a logging logic in a custom function called when change detection evaluates expressions:
<>Copy@Component({ selector: 'r-comp', template: `{{addRender()}}` }) export class RComponent { addRender() { calls.render.push('R'); } }
And this time I got different results:


It’s a proper depth-first graph traversal algorithm. So what’s going on here? It’s actually pretty simple, let me explain.
Change detection operationsLink to this section
To understand the difference in behavior we need to take a look at the operations performed by change detection mechanism when checking a component. If you’ve read my other articles on change detection you probably know that the key operations performed by change detection are the following:
- update child components properties
- call
NgOnChanges
andNgDoCheck
lifecycle hooks on child components - update DOM on the current component
- run change detection for child components
I highlighted one interesting specifics above — when Angular checks the current component it calls lifecycle hooks on child components, but renders DOM for the current component. And that’s a very important distinction. This is precisely the reason that makes it seem as if the algorithm runs breadth-first if we put logging into NgDoCheck hook. When Angular checks a current component it calls lifecycle hooks for all its child components which are siblings. Suppose Angular checks K
component now and calls NgDoCheck
lifecycle hook on L
and C
. So, we get the following:


Looks like breadth-first algorithm. However, remember that Angular still in the process of checking K
component. So after completing all operations for the K
component it doesn’t proceed to checking the sibling V
component, as it would with the breadth-first implementation. Instead, it goes on to check L
component, which is a child of K
. This is the depth-first implementation of change detection algorithm. And as we now know it will call ngDoCheck
on J
and O
components and this is exactly what happens:


So, after all, my gut didn’t let me down. Change detection mechanism is implemented as depth-first internally, but involves calling ngDoCheck
lifecycle hooks on sibling components first. By the way, I already described this logic in depth in the If you think `ngDoCheck` means your component is being checked — read this article.
Stackblitz demoLink to this section
Here you can see the demo with logging logic in different places.
Comments (0)
Be the first to leave a comment
About the author

Principal Engineer at kawa.ai.. Founder indepth.dev. Big fan of software engineering, Web Platform & JavaScript. Man of Science & Philosophy.

About the author
Max Koretskyi
Principal Engineer at kawa.ai.. Founder indepth.dev. Big fan of software engineering, Web Platform & JavaScript. Man of Science & Philosophy.
About the author

Principal Engineer at kawa.ai.. Founder indepth.dev. Big fan of software engineering, Web Platform & JavaScript. Man of Science & Philosophy.