Been messing with this for way too long. I am gonna show some code that's overly complicated because I wanted to exclude some potential culprits. My Angular component: import { Component, OnInit } from '@angular/core'; import { EsrsDocumentService } from '../esrs-document.service'; import { Router, ActivatedRoute } from '@angular/router'; import { EsrsNode, EsrsTreeNode, SectionNode } from '../esrs-tree-node.model'; import {NestedTreeControl} from '@angular/cdk/tree'; import {MatTreeNestedDataSource} from '@angular/material/tree'; @Component({ selector: 'app-esrs-document-detail', templateUrl: './esrs-document-detail.component.html', styleUrls: ['./esrs-document-detail.component.css'] }) export class EsrsDocumentDetailComponent implements OnInit { treeControl = new NestedTreeControl<EsrsTreeNode>(node => node.children); dataSource = new MatTreeNestedDataSource<EsrsTreeNode>(); constructor( private esrsDocumentService: EsrsDocumentService, private activatedRoute: ActivatedRoute ){} ngOnInit(): void { const docId = this.activatedRoute.snapshot.paramMap.get('id'); // init datasource.data with esrs nodes this.esrsDocumentService.getEsrsNodes(docId).subscribe((nodes: EsrsNode[]) => { console.log("EsrsDocumentDetailComponent ngOnInit", nodes); this.dataSource.data = nodes.map(n => ({ ...n, hasChild: n.hasChild, children: [] })); }); } loadChildren(node: EsrsNode) { if (node.children && node.children.length > 0) return; const docId = this.activatedRoute.snapshot.paramMap.get('id'); this.esrsDocumentService.getSectionNodes(docId, node.id) .subscribe((sections: SectionNode[]) => { const treeNode = this.findNodeById(this.dataSource.data, node.id); if (treeNode) { // Cambia la referenza dei figli treeNode.children = sections.map(section => ({ ...section, hasChild: false, children: [] })); // Cambia la referenza del nodo padre nell'array radice const idx = this.dataSource.data.findIndex(n => n.id === treeNode.id); if (idx !== -1) { this.dataSource.data[idx] = { ...treeNode }; } this.treeControl.dataNodes = this.dataSource.data; this.treeControl.expand(treeNode); this.dataSource.data = this.dataSource.data.slice(); } }); } // Trova il nodo nell'albero per id (ricorsivo) private findNodeById(nodes: EsrsTreeNode[], id: string): EsrsTreeNode | null { for (const node of nodes) { if (node.id === id) return node; if (node.children && node.children.length > 0) { const found = this.findNodeById(node.children, id); if (found) return found; } } return null; } trackById(index: number, node: any): string { return node.id; } hasChild = (_: number, node: EsrsTreeNode) => !!(node.children && node.children.length > 0 || node.hasChild); } then its HTML file, please note the initial <pre>{{ dataSource.data | json }}</pre> <pre>{{ dataSource.data | json }}</pre> <mat-tree [dataSource]="dataSource" [treeControl]="treeControl" class="example-tree" [trackBy]="trackById"> <!-- Nodo foglia --> <mat-tree-node *matTreeNodeDef="let node"> {{ node.section || node.label || node.esrs }} </mat-tree-node> <!-- Nodo espandibile --> <mat-nested-tree-node *matTreeNodeDef="let node; when: hasChild"> <div class="mat-tree-node"> <button mat-icon-button matTreeNodeToggle [attr.aria-label]="'Toggle ' + (node.section || node.label || node.esrs)" (click)="loadChildren(node)"> <mat-icon class="mat-icon-rtl-mirror"> {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}} </mat-icon> </button> {{ node.section || node.label || node.esrs }} </div> <div role="group"> <ng-container matTreeNodeOutlet></ng-container> </div> </mat-nested-tree-node> </mat-tree> Now the first screenshot before expanding the node and then what's shown after expanding: As you can the children[] is perfectly pupulated however but they are not being shown as part pf <mat-tree>. Here is the HTML taken from the page: <mat-tree _ngcontent-kya-c262="" role="tree" class="mat-tree cdk-tree example-tree" ng-reflect-data-source="[object Object]" ng-reflect-tree-control="[object Object]" ng-reflect-track-by="trackById(index, node) { " > <mat-nested-tree-node _ngcontent-kya-c262="" class="cdk-tree-node cdk-nested-tree-node mat-nested-tree-node ng-star-inserted" role="treeitem" aria-level="1" aria-expanded="true"> <div _ngcontent-kya-c262="" class="mat-tree-node"> <button _ngcontent-kya-c262="" mat-icon-button="" mattreenodetoggle="" class="mat-focus-indicator mat-icon-button mat-button-base" aria-label="Toggle ESRS E1 - Cambiamento climatico"> <span class="mat-button-wrapper"> <mat-icon _ngcontent-kya-c262="" role="img" class="mat-icon notranslate mat-icon-rtl-mirror material-icons mat-icon-no-color" aria-hidden="true" data-mat-icon-type="font"> expand_more </mat-icon> </span> <span matripple="" class="mat-ripple mat-button-ripple mat-button-ripple-round" ng-reflect-disabled="false" ng-reflect-centered="true" ng-reflect-trigger="[object HTMLButtonElement]"></span> <span class="mat-button-focus-overlay"></span> </button> ESRS E1 - Cambiamento climatico </div> <div _ngcontent-kya-c262="" role="group"><!--ng-container--></div> </mat-nested-tree-node> <!--ng-container--> </mat-tree> why the hell am I not seeing no <mat-tree-node> at all?! Continue reading...