diff --git a/src/frontend/app/app-common.module.ts b/src/frontend/app/app-common.module.ts
index 41825d2db29527ccd61274dbdc55eb3229b13019..e21735705ce75669fe3b552cafd7d81f1ae44d7d 100755
--- a/src/frontend/app/app-common.module.ts
+++ b/src/frontend/app/app-common.module.ts
@@ -28,6 +28,7 @@ import { FullDatePipe } from '../plugins/fullDate.pipe';
 import { SafeHtmlPipe } from '../plugins/safeHtml.pipe';
 import { SecureUrlPipe } from '../plugins/secureUrl.pipe';
 import { EcplOnlyofficeViewerComponent } from '../plugins/onlyoffice-api-js/onlyoffice-viewer.component';
+import { MaarchTreeComponent } from '../plugins/tree/maarch-tree.component';
 
 /*FRONT IMPORTS*/
 import { AppMaterialModule } from './app-material.module';
@@ -134,7 +135,8 @@ export class MyHammerConfig extends HammerGestureConfig {
         HistoryComponent,
         AddressBanAutocompleteComponent,
         VisaWorkflowComponent,
-        AvisWorkflowComponent
+        AvisWorkflowComponent,
+        MaarchTreeComponent
     ],
     exports: [
         CommonModule,
@@ -180,7 +182,8 @@ export class MyHammerConfig extends HammerGestureConfig {
         HistoryComponent,
         AddressBanAutocompleteComponent,
         VisaWorkflowComponent,
-        AvisWorkflowComponent
+        AvisWorkflowComponent,
+        MaarchTreeComponent
     ],
     providers: [
         { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
diff --git a/src/frontend/plugins/tree/maarch-tree.component.html b/src/frontend/plugins/tree/maarch-tree.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..4fe265840ef289f22a07b7a30e3179415e422692
--- /dev/null
+++ b/src/frontend/plugins/tree/maarch-tree.component.html
@@ -0,0 +1,14 @@
+<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
+    <mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
+        <button mat-icon-button disabled></button>
+        {{getData(node.item).label}}
+    </mat-tree-node>
+    <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
+        <button type="button" mat-icon-button [attr.aria-label]="'toggle ' + node.filename" matTreeNodeToggle>
+            <mat-icon class="mat-icon-rtl-mirror fa fa-{{treeControl.isExpanded(node) ? 'chevron-down' : 'chevron-right'}}"></mat-icon>
+        </button>
+        {{getData(node.item).label}}
+        <mat-progress-bar *ngIf="node.isLoading" mode="indeterminate" class="example-tree-progress-bar">
+        </mat-progress-bar>
+    </mat-tree-node>
+</mat-tree>
\ No newline at end of file
diff --git a/src/frontend/plugins/tree/maarch-tree.component.scss b/src/frontend/plugins/tree/maarch-tree.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..db44984097748fabd6c313bc24639a63b09e3ad2
--- /dev/null
+++ b/src/frontend/plugins/tree/maarch-tree.component.scss
@@ -0,0 +1,3 @@
+.example-tree-progress-bar {
+    margin-left: 30px;
+}
\ No newline at end of file
diff --git a/src/frontend/plugins/tree/maarch-tree.component.ts b/src/frontend/plugins/tree/maarch-tree.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f0db38c6b516aec1fad43d0e24993cac6682347
--- /dev/null
+++ b/src/frontend/plugins/tree/maarch-tree.component.ts
@@ -0,0 +1,284 @@
+import {
+    CollectionViewer,
+    SelectionChange,
+    DataSource
+} from '@angular/cdk/collections';
+import { FlatTreeControl } from '@angular/cdk/tree';
+import { Component, Injectable, Input, OnInit } from '@angular/core';
+import { BehaviorSubject, merge, Observable } from 'rxjs';
+import { map, tap } from 'rxjs/operators';
+import { HttpClient } from '@angular/common/http';
+
+/** Flat node with expandable and level information */
+export class DynamicFlatNode {
+    constructor(
+        public item: string,
+        public level = 1,
+        public expandable = false,
+        public isLoading = false
+    ) { }
+}
+
+/**
+ * Database for dynamic data. When expanding a node in the tree, the data source will need to fetch
+ * the descendants data from the database.
+ */
+@Injectable({ providedIn: 'root' })
+export class DynamicDatabase {
+    dataMap = new Map<string, string[]>([]);
+
+    rootLevelNodes: string[] = [];
+
+    /** Initial data from database */
+    initialData(): DynamicFlatNode[] {
+        return this.rootLevelNodes.map(name => new DynamicFlatNode(name, 0, true));
+    }
+
+    setData(node: any) {
+        console.log(node.childrens);
+        return this.dataMap.set(node.id, node.childrens);
+    }
+
+    setRootNode(rootnodes: string[]) {
+        this.rootLevelNodes = rootnodes;
+    }
+
+    getChildren(node: string): string[] | undefined {
+        return this.dataMap.get(node);
+    }
+
+    isExpandable(node: string): boolean {
+        return this.dataMap.has(node);
+    }
+}
+/**
+ * File database, it can build a tree structured Json object from string.
+ * Each node in Json object represents a file or a directory. For a file, it has filename and type.
+ * For a directory, it has filename and children (a list of files or directories).
+ * The input will be a json object string, and the output is a list of `FileNode` with nested
+ * structure.
+ */
+export class DynamicDataSource implements DataSource<DynamicFlatNode> {
+    dataChange = new BehaviorSubject<DynamicFlatNode[]>([]);
+
+    get data(): DynamicFlatNode[] {
+        return this.dataChange.value;
+    }
+    set data(value: DynamicFlatNode[]) {
+        this._treeControl.dataNodes = value;
+        this.dataChange.next(value);
+    }
+
+    constructor(
+        private _treeControl: FlatTreeControl<DynamicFlatNode>,
+        private _database: DynamicDatabase,
+        private rawData: any,
+        private httpClient: HttpClient
+    ) { }
+
+    connect(collectionViewer: CollectionViewer): Observable<DynamicFlatNode[]> {
+        this._treeControl.expansionModel.changed.subscribe(change => {
+            if (
+                (change as SelectionChange<DynamicFlatNode>).added ||
+                (change as SelectionChange<DynamicFlatNode>).removed
+            ) {
+                this.handleTreeControl(change as SelectionChange<DynamicFlatNode>);
+            }
+        });
+
+        return merge(collectionViewer.viewChange, this.dataChange).pipe(
+            map(() => this.data)
+        );
+    }
+
+    disconnect(collectionViewer: CollectionViewer): void { }
+
+    /** Handle expand/collapse behaviors */
+    handleTreeControl(change: SelectionChange<DynamicFlatNode>) {
+        if (change.added) {
+            change.added.forEach(node => this.toggleNode(node, true));
+        }
+        if (change.removed) {
+            change.removed
+                .slice()
+                .reverse()
+                .forEach(node => this.toggleNode(node, false));
+        }
+    }
+
+    /**
+     * Toggle the node, remove from display list
+     */
+    toggleNode(node: DynamicFlatNode, expand: boolean) {
+        let children = this._database.getChildren(node.item);
+        let index = this.data.indexOf(node);
+        if (!this.rawData[index].hasChildren) {
+            // If no children, or cannot find the node, no op
+            return;
+        }
+
+        node.isLoading = true;
+
+        if (expand) {
+            if (children === undefined) {
+                this.httpClient
+                    .get('../rest/entities')
+                    .pipe(
+                        tap(() => {
+                            console.log(node);
+                            this.rawData.push({
+                                id: 34,
+                                label: 'cool',
+                                parent_id: node.item,
+                                hasChildren: false
+                            });
+
+                            const folders = this.rawData.map((elem: any) => elem.id);
+
+                            this.rawData.forEach((element: any) => {
+                                const node = {
+                                    id: element.id,
+                                    childrens: this.rawData
+                                        .filter((elem: any) => elem.parent_id === element.id)
+                                        .map((elem: any) => elem.id)
+                                };
+                                if (
+                                    this.rawData.filter((elem: any) => elem.parent_id === element.id).length > 0
+                                ) {
+                                    this._database.setData(node);
+                                }
+                            });
+
+                            children = this._database.getChildren(node.item);
+                            index = this.data.indexOf(node);
+
+                            const nodes = children.map(
+                                name =>
+                                    new DynamicFlatNode(
+                                        name,
+                                        node.level + 1,
+                                        this._database.isExpandable(name)
+                                    )
+                            );
+                            console.log(nodes);
+                            this.data.splice(index + 1, 0, ...nodes);
+                            // notify the change
+                            this.dataChange.next(this.data);
+                            node.isLoading = false;
+                        })
+                    )
+                    .subscribe();
+            } else {
+                const nodes = children.map(
+                    name =>
+                        new DynamicFlatNode(
+                            name,
+                            node.level + 1,
+                            this._database.isExpandable(name)
+                        )
+                );
+                console.log(nodes);
+                this.data.splice(index + 1, 0, ...nodes);
+                // notify the change
+                this.dataChange.next(this.data);
+                node.isLoading = false;
+            }
+        } else {
+            let count = 0;
+            for (
+                let i = index + 1;
+                i < this.data.length && this.data[i].level > node.level;
+                i++, count++
+            ) { }
+            this.data.splice(index + 1, count);
+            // notify the change
+            this.dataChange.next(this.data);
+            node.isLoading = false;
+        }
+    }
+}
+
+/**
+ * @title Tree with dynamic data
+ */
+@Component({
+    selector: 'app-maaarch-tree',
+    templateUrl: 'maarch-tree.component.html',
+    styleUrls: ['maarch-tree.component.scss']
+})
+export class MaarchTreeComponent implements OnInit {
+
+    @Input() rawData: any = [];
+
+    constructor(
+        private database: DynamicDatabase,
+        private httpClient: HttpClient
+    ) {
+        this.treeControl = new FlatTreeControl<DynamicFlatNode>(
+            this.getLevel,
+            this.isExpandable
+        );
+    }
+
+    treeControl: FlatTreeControl<DynamicFlatNode>;
+
+    dataSource: DynamicDataSource;
+
+    getLevel = (node: DynamicFlatNode) => node.level;
+
+    isExpandable = (node: DynamicFlatNode) => node.expandable;
+
+    hasChild = (_: number, _nodeData: DynamicFlatNode) =>
+        this.getData(_nodeData.item).hasChildren
+
+    ngOnInit(): void {
+        console.log('init!');
+        // SAMPLE
+        this.rawData = [
+            {
+                id: '46',
+                label: 'bonjour',
+                parent_id: null,
+                hasChildren: true
+            },
+            {
+                id: '1',
+                label: 'Compétences fonctionnelles',
+                parent_id: null,
+                hasChildren: false
+            }
+        ];
+        this.dataSource = new DynamicDataSource(
+            this.treeControl,
+            this.database,
+            this.rawData,
+            this.httpClient
+        );
+        this.initTree();
+    }
+
+    initTree() {
+        this.rawData.forEach((element: any) => {
+            const node = {
+                id: element.id,
+                childrens: this.rawData
+                    .filter((elem: any) => elem.parent_id === element.id)
+                    .map((elem: any) => elem.id)
+            };
+            if (
+                this.rawData.filter((elem: any) => elem.parent_id === element.id).length > 0
+            ) {
+                this.database.setData(node);
+            }
+        });
+
+        this.database.setRootNode(
+            this.rawData.filter((elem: any) => elem.parent_id === null).map((elem: any) => elem.id)
+        );
+        this.dataSource.data = this.database.initialData();
+    }
+
+    getData(id: any) {
+        return this.rawData.filter((elem: any) => elem.id === id)[0];
+    }
+}