JavaScript/jQuery TreeList - Getting Started

jQuery
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.
Angular
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.
Vue
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.
React
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.

The TreeList component uses a multi-column tree view to display data from a local or remote storage and allows users to sort, group, filter, and perform other data operations.

This tutorial shows how to add the TreeList to a page, bind it to data, and configure its core features. As a result, you will get a UI component that looks as follows:

Each section in this tutorial covers a single configuration step. You can also find the full code in the following GitHub repository: getting-started-with-treelist.

Create a TreeList

jQuery

Add DevExtreme to your jQuery application and use the following code to create a TreeList:

index.js
index.html
index.css
$(function() {
    $("#treeList").dxTreeList({
        // Configuration goes here
    });
});
<html>
    <head>
        <!-- ... -->
        <script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>

        <link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/21.2.15/css/dx.light.css">
        <link rel="stylesheet" href="index.css">

        <script type="text/javascript" src="https://cdn3.devexpress.com/jslib/21.2.15/js/dx.all.js"></script>
        <script type="text/javascript" src="index.js"></script>
    </head>
    <body class="dx-viewport">
        <div id="treeList"></div>
    </body>
</html>
#treeList {
    height: 500px;
}
Angular

Add DevExtreme to your Angular application and use the following code to create a TreeList:

app.component.html
app.component.ts
app.module.ts
app.component.css
<dx-tree-list id="treeList"
    <!-- Configuration goes here -->
>
</dx-tree-list>
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {

}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import { DxTreeListModule } from 'devextreme-angular';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        DxTreeListModule
    ],
    providers: [ ],
    bootstrap: [AppComponent]
})
export class AppModule { }
#treeList {
    height: 500px;
}
Vue

Add DevExtreme to your Vue application and use the following code to create a TreeList:

App.vue
<template>
    <div id="app-container">
        <DxTreeList id="treeList">
            <!-- Configuration goes here -->
        </DxTreeList>
    </div>
</template>

<script>
import 'devextreme/dist/css/dx.light.css';

import { DxTreeList } from 'devextreme-vue/tree-list';

export default {
    components: {
        DxTreeList
    }
}
</script>

<style>
#treeList {
    height: 500px;
}
</style>
React

Add DevExtreme to your React application and use the following code to create a TreeList:

App.js
App.css
import React from 'react';
import 'devextreme/dist/css/dx.light.css';

import { TreeList } from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList id="treeList">
                {/* Configuration goes here */}
            </TreeList>
        </div>
    );
}

export default App;
#treeList {
    height: 500px;
}

Bind the TreeList to Data

TreeList supports plain and hierarchical data structures. If a data source has a hierarchical structure, set the dataStructure property to "tree" and use the itemsExpr property to specify the data field that contains child nodes. The TreeList automatically generates identifiers for all nodes and links them with each other. Refer to the following demo for more information: Simple Array: Hierarchical Structure.

In a plain data structure, each node must have a unique identifier and a reference to its parent node. To specify data fields that store the identifiers and the references, use the keyExpr and parentIdExpr properties. Top-level nodes descend from the root node. To specify its identifier, use the rootValue property. In this tutorial, we use a plain data structure.

The TreeList component can load and update data from different data source types. To use a local array as in this tutorial, assign it to the dataSource property. If you want to use another data source type, refer to one of the following articles:

jQuery
index.js
employees.js
$(function() {
    $("#treeList").dxTreeList({
        dataSource: employees,
        rootValue: -1,
        keyExpr: "ID",
        parentIdExpr: "HeadID"
    });
});
const employees = [{ 
    "ID": 1,
    "HeadID": -1,
    "FullName": "John Heart",
    "Prefix": "Mr.",
    "Position": "CEO",
    "City": "Los Angeles",
    "State": "California",
    "Email": "jheart@dx-email.com",
    "Skype": "jheart_DX_skype",
    "MobilePhone": "(213) 555-9392",
    "BirthDate": "1964-03-16",
    "HireDate": "1995-01-15"
}, {
    "ID": 2,
    "HeadID": 1,
    "FullName": "Samantha Bright",
    "Prefix": "Dr.",
    "Position": "COO",
    "City": "Los Angeles",
    "State": "California",
    "Email": "samanthab@dx-email.com",
    "Skype": "samanthab_DX_skype",
    "MobilePhone": "(213) 555-2858",
    "BirthDate": "1966-05-02",
    "HireDate": "2004-05-24"
}, {
    "ID": 3,
    "HeadID": 1,
    "FullName": "Arthur Miller",
    "Prefix": "Mr.",
    "Position": "CTO",
    "City": "Denver",
    "State": "Colorado",
    "Email": "arthurm@dx-email.com",
    "Skype": "arthurm_DX_skype",
    "MobilePhone": "(310) 555-8583",
    "BirthDate": "1972-07-11",
    "HireDate": "2007-12-18"
}, {
    "ID": 4,
    "HeadID": 1,
    "FullName": "Robert Reagan",
    "Prefix": "Mr.",
    "Position": "CMO",
    "City": "Bentonville",
    "State": "Arkansas",
    "Email": "robertr@dx-email.com",
    "Skype": "robertr_DX_skype",
    "MobilePhone": "(818) 555-2387",
    "BirthDate": "1974-09-07",
    "HireDate": "2002-11-08"
}, {
    "ID": 5,
    "HeadID": 1,
    "FullName": "Greta Sims",
    "Prefix": "Ms.",
    "Position": "HR Manager",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "gretas@dx-email.com",
    "Skype": "gretas_DX_skype",
    "MobilePhone": "(818) 555-6546",
    "BirthDate": "1977-11-22",
    "HireDate": "1998-04-23"
}, {
    "ID": 6,
    "HeadID": 3,
    "FullName": "Brett Wade",
    "Prefix": "Mr.",
    "Position": "IT Manager",
    "City": "Reno",
    "State": "Nevada",
    "Email": "brettw@dx-email.com",
    "Skype": "brettw_DX_skype",
    "MobilePhone": "(626) 555-0358",
    "BirthDate": "1968-12-01",
    "HireDate": "2009-03-06"
}, {
    "ID": 7,
    "HeadID": 5,
    "FullName": "Sandra Johnson",
    "Prefix": "Mrs.",
    "Position": "Controller",
    "City": "Beaver",
    "State": "Utah",
    "Email": "sandraj@dx-email.com",
    "Skype": "sandraj_DX_skype",
    "MobilePhone": "(562) 555-2082",
    "BirthDate": "1974-11-15",
    "HireDate": "2005-05-11"
}, {
    "ID": 8,
    "HeadID": 4,
    "FullName": "Ed Holmes",
    "Prefix": "Dr.",
    "Position": "Sales Manager",
    "City": "Malibu",
    "State": "California",
    "Email": "edwardh@dx-email.com",
    "Skype": "edwardh_DX_skype",
    "MobilePhone": "(310) 555-1288",
    "BirthDate": "1973-07-14",
    "HireDate": "2005-06-19"
}, {
    "ID": 9,
    "HeadID": 3,
    "FullName": "Barb Banks",
    "Prefix": "Mrs.",
    "Position": "Support Manager",
    "City": "Phoenix",
    "State": "Arizona",
    "Email": "barbarab@dx-email.com",
    "Skype": "barbarab_DX_skype",
    "MobilePhone": "(310) 555-3355",
    "BirthDate": "1979-04-14",
    "HireDate": "2002-08-07"
}, {
    "ID": 10,
    "HeadID": 2,
    "FullName": "Kevin Carter",
    "Prefix": "Mr.",
    "Position": "Shipping Manager",
    "City": "San Diego",
    "State": "California",
    "Email": "kevinc@dx-email.com",
    "Skype": "kevinc_DX_skype",
    "MobilePhone": "(213) 555-2840",
    "BirthDate": "1978-01-09",
    "HireDate": "2009-08-11"
}, {
    "ID": 11,
    "HeadID": 5,
    "FullName": "Cindy Stanwick",
    "Prefix": "Ms.",
    "Position": "HR Assistant",
    "City": "Little Rock",
    "State": "Arkansas",
    "Email": "cindys@dx-email.com",
    "Skype": "cindys_DX_skype",
    "MobilePhone": "(818) 555-6655",
    "BirthDate": "1985-06-05",
    "HireDate": "2008-03-24"
}, {
    "ID": 12,
    "HeadID": 8,
    "FullName": "Sammy Hill",
    "Prefix": "Mr.",
    "Position": "Sales Assistant",
    "City": "Pasadena",
    "State": "California",
    "Email": "sammyh@dx-email.com",
    "Skype": "sammyh_DX_skype",
    "MobilePhone": "(626) 555-7292",
    "BirthDate": "1984-02-17",
    "HireDate": "2012-02-01"
}, {
    "ID": 13,
    "HeadID": 10,
    "FullName": "Davey Jones",
    "Prefix": "Mr.",
    "Position": "Shipping Assistant",
    "City": "Pasadena",
    "State": "California",
    "Email": "davidj@dx-email.com",
    "Skype": "davidj_DX_skype",
    "MobilePhone": "(626) 555-0281",
    "BirthDate": "1983-03-06",
    "HireDate": "2011-04-24"
}, {
    "ID": 14,
    "HeadID": 10,
    "FullName": "Victor Norris",
    "Prefix": "Mr.",
    "Position": "Shipping Assistant",
    "City": "Little Rock",
    "State": "Arkansas",
    "Email": "victorn@dx-email.com",
    "Skype": "victorn_DX_skype",
    "MobilePhone": "(213) 555-9278",
    "BirthDate": "1986-07-23",
    "HireDate": "2012-07-23"
}, {
    "ID": 15,
    "HeadID": 10,
    "FullName": "Mary Stern",
    "Prefix": "Ms.",
    "Position": "Shipping Assistant",
    "City": "Beaver",
    "State": "Utah",
    "Email": "marys@dx-email.com",
    "Skype": "marys_DX_skype",
    "MobilePhone": "(818) 555-7857",
    "BirthDate": "1982-04-08",
    "HireDate": "2012-08-12"
}, {
    "ID": 16,
    "HeadID": 10,
    "FullName": "Robin Cosworth",
    "Prefix": "Mrs.",
    "Position": "Shipping Assistant",
    "City": "Los Angeles",
    "State": "California",
    "Email": "robinc@dx-email.com",
    "Skype": "robinc_DX_skype",
    "MobilePhone": "(818) 555-0942",
    "BirthDate": "1981-06-12",
    "HireDate": "2012-09-01"
}, {
    "ID": 17,
    "HeadID": 9,
    "FullName": "Kelly Rodriguez",
    "Prefix": "Ms.",
    "Position": "Support Assistant",
    "City": "Boise",
    "State": "Idaho",
    "Email": "kellyr@dx-email.com",
    "Skype": "kellyr_DX_skype",
    "MobilePhone": "(818) 555-9248",
    "BirthDate": "1988-05-11",
    "HireDate": "2012-10-13"
}, {
    "ID": 18,
    "HeadID": 9,
    "FullName": "James Anderson",
    "Prefix": "Mr.",
    "Position": "Support Assistant",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "jamesa@dx-email.com",
    "Skype": "jamesa_DX_skype",
    "MobilePhone": "(323) 555-4702",
    "BirthDate": "1987-01-29",
    "HireDate": "2012-10-18"
}, {
    "ID": 19,
    "HeadID": 9,
    "FullName": "Antony Remmen",
    "Prefix": "Mr.",
    "Position": "Support Assistant",
    "City": "Boise",
    "State": "Idaho",
    "Email": "anthonyr@dx-email.com",
    "Skype": "anthonyr_DX_skype",
    "MobilePhone": "(310) 555-6625",
    "BirthDate": "1986-02-19",
    "HireDate": "2013-01-19"
}, {
    "ID": 20,
    "HeadID": 8,
    "FullName": "Olivia Peyton",
    "Prefix": "Mrs.",
    "Position": "Sales Assistant",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "oliviap@dx-email.com",
    "Skype": "oliviap_DX_skype",
    "MobilePhone": "(310) 555-2728",
    "BirthDate": "1981-06-03",
    "HireDate": "2012-05-14"
}, {
    "ID": 21,
    "HeadID": 6,
    "FullName": "Taylor Riley",
    "Prefix": "Mr.",
    "Position": "Network Admin",
    "City": "San Jose",
    "State": "California",
    "Email": "taylorr@dx-email.com",
    "Skype": "taylorr_DX_skype",
    "MobilePhone": "(310) 555-7276",
    "BirthDate": "1982-08-14",
    "HireDate": "2012-04-14"
}, {
    "ID": 22,
    "HeadID": 6,
    "FullName": "Amelia Harper",
    "Prefix": "Mrs.",
    "Position": "Network Admin",
    "City": "Los Angeles",
    "State": "California",
    "Email": "ameliah@dx-email.com",
    "Skype": "ameliah_DX_skype",
    "MobilePhone": "(213) 555-4276",
    "BirthDate": "1983-11-19",
    "HireDate": "2011-02-10"
}, {
    "ID": 23,
    "HeadID": 6,
    "FullName": "Wally Hobbs",
    "Prefix": "Mr.",
    "Position": "Programmer",
    "City": "Chatsworth",
    "State": "California",
    "Email": "wallyh@dx-email.com",
    "Skype": "wallyh_DX_skype",
    "MobilePhone": "(818) 555-8872",
    "BirthDate": "1984-12-24",
    "HireDate": "2011-02-17"
}, {
    "ID": 24,
    "HeadID": 6,
    "FullName": "Brad Jameson",
    "Prefix": "Mr.",
    "Position": "Programmer",
    "City": "San Fernando",
    "State": "California",
    "Email": "bradleyj@dx-email.com",
    "Skype": "bradleyj_DX_skype",
    "MobilePhone": "(818) 555-4646",
    "BirthDate": "1988-10-12",
    "HireDate": "2011-03-02"
}, {
    "ID": 25,
    "HeadID": 6,
    "FullName": "Karen Goodson",
    "Prefix": "Miss",
    "Position": "Programmer",
    "City": "South Pasadena",
    "State": "California",
    "Email": "kareng@dx-email.com",
    "Skype": "kareng_DX_skype",
    "MobilePhone": "(626) 555-0908",
    "BirthDate": "1987-04-26",
    "HireDate": "2011-03-14"
}, {
    "ID": 26,
    "HeadID": 5,
    "FullName": "Marcus Orbison",
    "Prefix": "Mr.",
    "Position": "Travel Coordinator",
    "City": "Los Angeles",
    "State": "California",
    "Email": "marcuso@dx-email.com",
    "Skype": "marcuso_DX_skype",
    "MobilePhone": "(213) 555-7098",
    "BirthDate": "1982-03-02",
    "HireDate": "2005-05-19"
}, {
    "ID": 27,
    "HeadID": 5,
    "FullName": "Sandy Bright",
    "Prefix": "Ms.",
    "Position": "Benefits Coordinator",
    "City": "Denver",
    "State": "Colorado",
    "Email": "sandrab@dx-email.com",
    "Skype": "sandrab_DX_skype",
    "MobilePhone": "(818) 555-0524",
    "BirthDate": "1983-09-11",
    "HireDate": "2005-06-04"
}, {
    "ID": 28,
    "HeadID": 6,
    "FullName": "Morgan Kennedy",
    "Prefix": "Mrs.",
    "Position": "Graphic Designer",
    "City": "San Fernando Valley",
    "State": "California",
    "Email": "morgank@dx-email.com",
    "Skype": "morgank_DX_skype",
    "MobilePhone": "(818) 555-8238",
    "BirthDate": "1984-07-17",
    "HireDate": "2012-01-11"
}, {
    "ID": 29,
    "HeadID": 28,
    "FullName": "Violet Bailey",
    "Prefix": "Ms.",
    "Position": "Jr Graphic Designer",
    "City": "La Canada",
    "State": "California",
    "Email": "violetb@dx-email.com",
    "Skype": "violetb_DX_skype",
    "MobilePhone": "(818) 555-2478",
    "BirthDate": "1985-06-10",
    "HireDate": "2012-01-19"
}, {
    "ID": 30,
    "HeadID": 5,
    "FullName": "Ken Samuelson",
    "Prefix": "Dr.",
    "Position": "Ombudsman",
    "City": "St. Louis",
    "State": "Missouri",
    "Email": "kents@dx-email.com",
    "Skype": "kents_DX_skype",
    "MobilePhone": "(562) 555-9282",
    "BirthDate": "1972-09-11",
    "HireDate": "2009-04-22"
}];
Angular
app.component.html
app.component.ts
employees.service.ts
<dx-tree-list
    [dataSource]="employees"
    [rootValue]="-1"
    keyExpr="ID"
    parentIdExpr="HeadID">
</dx-tree-list>
import { Component } from '@angular/core';
import { Employee, EmployeesService } from './employees.service';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    employees: Employee[] = [];

    constructor(service: EmployeesService) {
        this.employees = service.getEmployees();
    }
}
import { Injectable } from '@angular/core';

export interface Employee {
    ID: Number,
    HeadID: Number,
    FullName: String,
    Position: String,
    City: String,
    State: String,
    Email: String,
    Skype: String,
    MobilePhone: String,
    BirthDate: String,
    HireDate: String,
}

const employees: Employee[] = [{
    "ID": 1,
    "HeadID": -1,
    "FullName": "John Heart",
    "Position": "CEO",
    "City": "Los Angeles",
    "State": "California",
    "Email": "jheart@dx-email.com",
    "Skype": "jheart_DX_skype",
    "MobilePhone": "(213) 555-9392",
    "BirthDate": "1964-03-16",
    "HireDate": "1995-01-15"
}, {
    "ID": 2,
    "HeadID": 1,
    "FullName": "Samantha Bright",
    "Position": "COO",
    "City": "Los Angeles",
    "State": "California",
    "Email": "samanthab@dx-email.com",
    "Skype": "samanthab_DX_skype",
    "MobilePhone": "(213) 555-2858",
    "BirthDate": "1966-05-02",
    "HireDate": "2004-05-24"
}, {
    "ID": 3,
    "HeadID": 1,
    "FullName": "Arthur Miller",
    "Position": "CTO",
    "City": "Denver",
    "State": "Colorado",
    "Email": "arthurm@dx-email.com",
    "Skype": "arthurm_DX_skype",
    "MobilePhone": "(310) 555-8583",
    "BirthDate": "1972-07-11",
    "HireDate": "2007-12-18"
}, {
    "ID": 4,
    "HeadID": 1,
    "FullName": "Robert Reagan",
    "Position": "CMO",
    "City": "Bentonville",
    "State": "Arkansas",
    "Email": "robertr@dx-email.com",
    "Skype": "robertr_DX_skype",
    "MobilePhone": "(818) 555-2387",
    "BirthDate": "1974-09-07",
    "HireDate": "2002-11-08"
}, {
    "ID": 5,
    "HeadID": 1,
    "FullName": "Greta Sims",
    "Position": "HR Manager",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "gretas@dx-email.com",
    "Skype": "gretas_DX_skype",
    "MobilePhone": "(818) 555-6546",
    "BirthDate": "1977-11-22",
    "HireDate": "1998-04-23"
}, {
    "ID": 6,
    "HeadID": 3,
    "FullName": "Brett Wade",
    "Position": "IT Manager",
    "City": "Reno",
    "State": "Nevada",
    "Email": "brettw@dx-email.com",
    "Skype": "brettw_DX_skype",
    "MobilePhone": "(626) 555-0358",
    "BirthDate": "1968-12-01",
    "HireDate": "2009-03-06"
}, {
    "ID": 7,
    "HeadID": 5,
    "FullName": "Sandra Johnson",
    "Position": "Controller",
    "City": "Beaver",
    "State": "Utah",
    "Email": "sandraj@dx-email.com",
    "Skype": "sandraj_DX_skype",
    "MobilePhone": "(562) 555-2082",
    "BirthDate": "1974-11-15",
    "HireDate": "2005-05-11"
}, {
    "ID": 8,
    "HeadID": 4,
    "FullName": "Ed Holmes",
    "Position": "Sales Manager",
    "City": "Malibu",
    "State": "California",
    "Email": "edwardh@dx-email.com",
    "Skype": "edwardh_DX_skype",
    "MobilePhone": "(310) 555-1288",
    "BirthDate": "1973-07-14",
    "HireDate": "2005-06-19"
}, {
    "ID": 9,
    "HeadID": 3,
    "FullName": "Barb Banks",
    "Position": "Support Manager",
    "City": "Phoenix",
    "State": "Arizona",
    "Email": "barbarab@dx-email.com",
    "Skype": "barbarab_DX_skype",
    "MobilePhone": "(310) 555-3355",
    "BirthDate": "1979-04-14",
    "HireDate": "2002-08-07"
}, {
    "ID": 10,
    "HeadID": 2,
    "FullName": "Kevin Carter",
    "Position": "Shipping Manager",
    "City": "San Diego",
    "State": "California",
    "Email": "kevinc@dx-email.com",
    "Skype": "kevinc_DX_skype",
    "MobilePhone": "(213) 555-2840",
    "BirthDate": "1978-01-09",
    "HireDate": "2009-08-11"
}, {
    "ID": 11,
    "HeadID": 5,
    "FullName": "Cindy Stanwick",
    "Position": "HR Assistant",
    "City": "Little Rock",
    "State": "Arkansas",
    "Email": "cindys@dx-email.com",
    "Skype": "cindys_DX_skype",
    "MobilePhone": "(818) 555-6655",
    "BirthDate": "1985-06-05",
    "HireDate": "2008-03-24"
}, {
    "ID": 12,
    "HeadID": 8,
    "FullName": "Sammy Hill",
    "Position": "Sales Assistant",
    "City": "Pasadena",
    "State": "California",
    "Email": "sammyh@dx-email.com",
    "Skype": "sammyh_DX_skype",
    "MobilePhone": "(626) 555-7292",
    "BirthDate": "1984-02-17",
    "HireDate": "2012-02-01"
}, {
    "ID": 13,
    "HeadID": 10,
    "FullName": "Davey Jones",
    "Position": "Shipping Assistant",
    "City": "Pasadena",
    "State": "California",
    "Email": "davidj@dx-email.com",
    "Skype": "davidj_DX_skype",
    "MobilePhone": "(626) 555-0281",
    "BirthDate": "1983-03-06",
    "HireDate": "2011-04-24"
}, {
    "ID": 14,
    "HeadID": 10,
    "FullName": "Victor Norris",
    "Position": "Shipping Assistant",
    "City": "Little Rock",
    "State": "Arkansas",
    "Email": "victorn@dx-email.com",
    "Skype": "victorn_DX_skype",
    "MobilePhone": "(213) 555-9278",
    "BirthDate": "1986-07-23",
    "HireDate": "2012-07-23"
}, {
    "ID": 15,
    "HeadID": 10,
    "FullName": "Mary Stern",
    "Position": "Shipping Assistant",
    "City": "Beaver",
    "State": "Utah",
    "Email": "marys@dx-email.com",
    "Skype": "marys_DX_skype",
    "MobilePhone": "(818) 555-7857",
    "BirthDate": "1982-04-08",
    "HireDate": "2012-08-12"
}, {
    "ID": 16,
    "HeadID": 10,
    "FullName": "Robin Cosworth",
    "Position": "Shipping Assistant",
    "City": "Los Angeles",
    "State": "California",
    "Email": "robinc@dx-email.com",
    "Skype": "robinc_DX_skype",
    "MobilePhone": "(818) 555-0942",
    "BirthDate": "1981-06-12",
    "HireDate": "2012-09-01"
}, {
    "ID": 17,
    "HeadID": 9,
    "FullName": "Kelly Rodriguez",
    "Position": "Support Assistant",
    "City": "Boise",
    "State": "Idaho",
    "Email": "kellyr@dx-email.com",
    "Skype": "kellyr_DX_skype",
    "MobilePhone": "(818) 555-9248",
    "BirthDate": "1988-05-11",
    "HireDate": "2012-10-13"
}, {
    "ID": 18,
    "HeadID": 9,
    "FullName": "James Anderson",
    "Position": "Support Assistant",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "jamesa@dx-email.com",
    "Skype": "jamesa_DX_skype",
    "MobilePhone": "(323) 555-4702",
    "BirthDate": "1987-01-29",
    "HireDate": "2012-10-18"
}, {
    "ID": 19,
    "HeadID": 9,
    "FullName": "Antony Remmen",
    "Position": "Support Assistant",
    "City": "Boise",
    "State": "Idaho",
    "Email": "anthonyr@dx-email.com",
    "Skype": "anthonyr_DX_skype",
    "MobilePhone": "(310) 555-6625",
    "BirthDate": "1986-02-19",
    "HireDate": "2013-01-19"
}, {
    "ID": 20,
    "HeadID": 8,
    "FullName": "Olivia Peyton",
    "Position": "Sales Assistant",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "oliviap@dx-email.com",
    "Skype": "oliviap_DX_skype",
    "MobilePhone": "(310) 555-2728",
    "BirthDate": "1981-06-03",
    "HireDate": "2012-05-14"
}, {
    "ID": 21,
    "HeadID": 6,
    "FullName": "Taylor Riley",
    "Position": "Network Admin",
    "City": "San Jose",
    "State": "California",
    "Email": "taylorr@dx-email.com",
    "Skype": "taylorr_DX_skype",
    "MobilePhone": "(310) 555-7276",
    "BirthDate": "1982-08-14",
    "HireDate": "2012-04-14"
}, {
    "ID": 22,
    "HeadID": 6,
    "FullName": "Amelia Harper",
    "Position": "Network Admin",
    "City": "Los Angeles",
    "State": "California",
    "Email": "ameliah@dx-email.com",
    "Skype": "ameliah_DX_skype",
    "MobilePhone": "(213) 555-4276",
    "BirthDate": "1983-11-19",
    "HireDate": "2011-02-10"
}, {
    "ID": 23,
    "HeadID": 6,
    "FullName": "Wally Hobbs",
    "Position": "Programmer",
    "City": "Chatsworth",
    "State": "California",
    "Email": "wallyh@dx-email.com",
    "Skype": "wallyh_DX_skype",
    "MobilePhone": "(818) 555-8872",
    "BirthDate": "1984-12-24",
    "HireDate": "2011-02-17"
}, {
    "ID": 24,
    "HeadID": 6,
    "FullName": "Brad Jameson",
    "Position": "Programmer",
    "City": "San Fernando",
    "State": "California",
    "Email": "bradleyj@dx-email.com",
    "Skype": "bradleyj_DX_skype",
    "MobilePhone": "(818) 555-4646",
    "BirthDate": "1988-10-12",
    "HireDate": "2011-03-02"
}, {
    "ID": 25,
    "HeadID": 6,
    "FullName": "Karen Goodson",
    "Position": "Programmer",
    "City": "South Pasadena",
    "State": "California",
    "Email": "kareng@dx-email.com",
    "Skype": "kareng_DX_skype",
    "MobilePhone": "(626) 555-0908",
    "BirthDate": "1987-04-26",
    "HireDate": "2011-03-14"
}, {
    "ID": 26,
    "HeadID": 5,
    "FullName": "Marcus Orbison",
    "Position": "Travel Coordinator",
    "City": "Los Angeles",
    "State": "California",
    "Email": "marcuso@dx-email.com",
    "Skype": "marcuso_DX_skype",
    "MobilePhone": "(213) 555-7098",
    "BirthDate": "1982-03-02",
    "HireDate": "2005-05-19"
}, {
    "ID": 27,
    "HeadID": 5,
    "FullName": "Sandy Bright",
    "Position": "Benefits Coordinator",
    "City": "Denver",
    "State": "Colorado",
    "Email": "sandrab@dx-email.com",
    "Skype": "sandrab_DX_skype",
    "MobilePhone": "(818) 555-0524",
    "BirthDate": "1983-09-11",
    "HireDate": "2005-06-04"
}, {
    "ID": 28,
    "HeadID": 6,
    "FullName": "Morgan Kennedy",
    "Position": "Graphic Designer",
    "City": "San Fernando Valley",
    "State": "California",
    "Email": "morgank@dx-email.com",
    "Skype": "morgank_DX_skype",
    "MobilePhone": "(818) 555-8238",
    "BirthDate": "1984-07-17",
    "HireDate": "2012-01-11"
}, {
    "ID": 29,
    "HeadID": 28,
    "FullName": "Violet Bailey",
    "Position": "Jr Graphic Designer",
    "City": "La Canada",
    "State": "California",
    "Email": "violetb@dx-email.com",
    "Skype": "violetb_DX_skype",
    "MobilePhone": "(818) 555-2478",
    "BirthDate": "1985-06-10",
    "HireDate": "2012-01-19"
}, {
    "ID": 30,
    "HeadID": 5,
    "FullName": "Ken Samuelson",
    "Position": "Ombudsman",
    "City": "St. Louis",
    "State": "Missouri",
    "Email": "kents@dx-email.com",
    "Skype": "kents_DX_skype",
    "MobilePhone": "(562) 555-9282",
    "BirthDate": "1972-09-11",
    "HireDate": "2009-04-22"
}];

@Injectable({
    providedIn: 'root'
})
export class EmployeesService {
    getEmployees(): Employee[] {
        return employees;
    }
}
Vue
App.vue
employees.service.js
<template>
    <div id="app-container">
        <DxTreeList
            :data-source="employees"
            :root-value="-1"
            key-expr="ID"
            parent-id-expr="HeadID">
        </DxTreeList>
    </div>
</template>

<script>
// ...
import service from './employees.service';

export default {
    // ...
    data() {
        return {
            employees: service.getEmployees(),
        }
    },
}
</script>
const employees = [{
    "ID": 1,
    "HeadID": -1,
    "FullName": "John Heart",
    "Position": "CEO",
    "City": "Los Angeles",
    "State": "California",
    "Email": "jheart@dx-email.com",
    "Skype": "jheart_DX_skype",
    "MobilePhone": "(213) 555-9392",
    "BirthDate": "1964-03-16",
    "HireDate": "1995-01-15"
}, {
    "ID": 2,
    "HeadID": 1,
    "FullName": "Samantha Bright",
    "Position": "COO",
    "City": "Los Angeles",
    "State": "California",
    "Email": "samanthab@dx-email.com",
    "Skype": "samanthab_DX_skype",
    "MobilePhone": "(213) 555-2858",
    "BirthDate": "1966-05-02",
    "HireDate": "2004-05-24"
}, {
    "ID": 3,
    "HeadID": 1,
    "FullName": "Arthur Miller",
    "Position": "CTO",
    "City": "Denver",
    "State": "Colorado",
    "Email": "arthurm@dx-email.com",
    "Skype": "arthurm_DX_skype",
    "MobilePhone": "(310) 555-8583",
    "BirthDate": "1972-07-11",
    "HireDate": "2007-12-18"
}, {
    "ID": 4,
    "HeadID": 1,
    "FullName": "Robert Reagan",
    "Position": "CMO",
    "City": "Bentonville",
    "State": "Arkansas",
    "Email": "robertr@dx-email.com",
    "Skype": "robertr_DX_skype",
    "MobilePhone": "(818) 555-2387",
    "BirthDate": "1974-09-07",
    "HireDate": "2002-11-08"
}, {
    "ID": 5,
    "HeadID": 1,
    "FullName": "Greta Sims",
    "Position": "HR Manager",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "gretas@dx-email.com",
    "Skype": "gretas_DX_skype",
    "MobilePhone": "(818) 555-6546",
    "BirthDate": "1977-11-22",
    "HireDate": "1998-04-23"
}, {
    "ID": 6,
    "HeadID": 3,
    "FullName": "Brett Wade",
    "Position": "IT Manager",
    "City": "Reno",
    "State": "Nevada",
    "Email": "brettw@dx-email.com",
    "Skype": "brettw_DX_skype",
    "MobilePhone": "(626) 555-0358",
    "BirthDate": "1968-12-01",
    "HireDate": "2009-03-06"
}, {
    "ID": 7,
    "HeadID": 5,
    "FullName": "Sandra Johnson",
    "Position": "Controller",
    "City": "Beaver",
    "State": "Utah",
    "Email": "sandraj@dx-email.com",
    "Skype": "sandraj_DX_skype",
    "MobilePhone": "(562) 555-2082",
    "BirthDate": "1974-11-15",
    "HireDate": "2005-05-11"
}, {
    "ID": 8,
    "HeadID": 4,
    "FullName": "Ed Holmes",
    "Position": "Sales Manager",
    "City": "Malibu",
    "State": "California",
    "Email": "edwardh@dx-email.com",
    "Skype": "edwardh_DX_skype",
    "MobilePhone": "(310) 555-1288",
    "BirthDate": "1973-07-14",
    "HireDate": "2005-06-19"
}, {
    "ID": 9,
    "HeadID": 3,
    "FullName": "Barb Banks",
    "Position": "Support Manager",
    "City": "Phoenix",
    "State": "Arizona",
    "Email": "barbarab@dx-email.com",
    "Skype": "barbarab_DX_skype",
    "MobilePhone": "(310) 555-3355",
    "BirthDate": "1979-04-14",
    "HireDate": "2002-08-07"
}, {
    "ID": 10,
    "HeadID": 2,
    "FullName": "Kevin Carter",
    "Position": "Shipping Manager",
    "City": "San Diego",
    "State": "California",
    "Email": "kevinc@dx-email.com",
    "Skype": "kevinc_DX_skype",
    "MobilePhone": "(213) 555-2840",
    "BirthDate": "1978-01-09",
    "HireDate": "2009-08-11"
}, {
    "ID": 11,
    "HeadID": 5,
    "FullName": "Cindy Stanwick",
    "Position": "HR Assistant",
    "City": "Little Rock",
    "State": "Arkansas",
    "Email": "cindys@dx-email.com",
    "Skype": "cindys_DX_skype",
    "MobilePhone": "(818) 555-6655",
    "BirthDate": "1985-06-05",
    "HireDate": "2008-03-24"
}, {
    "ID": 12,
    "HeadID": 8,
    "FullName": "Sammy Hill",
    "Position": "Sales Assistant",
    "City": "Pasadena",
    "State": "California",
    "Email": "sammyh@dx-email.com",
    "Skype": "sammyh_DX_skype",
    "MobilePhone": "(626) 555-7292",
    "BirthDate": "1984-02-17",
    "HireDate": "2012-02-01"
}, {
    "ID": 13,
    "HeadID": 10,
    "FullName": "Davey Jones",
    "Position": "Shipping Assistant",
    "City": "Pasadena",
    "State": "California",
    "Email": "davidj@dx-email.com",
    "Skype": "davidj_DX_skype",
    "MobilePhone": "(626) 555-0281",
    "BirthDate": "1983-03-06",
    "HireDate": "2011-04-24"
}, {
    "ID": 14,
    "HeadID": 10,
    "FullName": "Victor Norris",
    "Position": "Shipping Assistant",
    "City": "Little Rock",
    "State": "Arkansas",
    "Email": "victorn@dx-email.com",
    "Skype": "victorn_DX_skype",
    "MobilePhone": "(213) 555-9278",
    "BirthDate": "1986-07-23",
    "HireDate": "2012-07-23"
}, {
    "ID": 15,
    "HeadID": 10,
    "FullName": "Mary Stern",
    "Position": "Shipping Assistant",
    "City": "Beaver",
    "State": "Utah",
    "Email": "marys@dx-email.com",
    "Skype": "marys_DX_skype",
    "MobilePhone": "(818) 555-7857",
    "BirthDate": "1982-04-08",
    "HireDate": "2012-08-12"
}, {
    "ID": 16,
    "HeadID": 10,
    "FullName": "Robin Cosworth",
    "Position": "Shipping Assistant",
    "City": "Los Angeles",
    "State": "California",
    "Email": "robinc@dx-email.com",
    "Skype": "robinc_DX_skype",
    "MobilePhone": "(818) 555-0942",
    "BirthDate": "1981-06-12",
    "HireDate": "2012-09-01"
}, {
    "ID": 17,
    "HeadID": 9,
    "FullName": "Kelly Rodriguez",
    "Position": "Support Assistant",
    "City": "Boise",
    "State": "Idaho",
    "Email": "kellyr@dx-email.com",
    "Skype": "kellyr_DX_skype",
    "MobilePhone": "(818) 555-9248",
    "BirthDate": "1988-05-11",
    "HireDate": "2012-10-13"
}, {
    "ID": 18,
    "HeadID": 9,
    "FullName": "James Anderson",
    "Position": "Support Assistant",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "jamesa@dx-email.com",
    "Skype": "jamesa_DX_skype",
    "MobilePhone": "(323) 555-4702",
    "BirthDate": "1987-01-29",
    "HireDate": "2012-10-18"
}, {
    "ID": 19,
    "HeadID": 9,
    "FullName": "Antony Remmen",
    "Position": "Support Assistant",
    "City": "Boise",
    "State": "Idaho",
    "Email": "anthonyr@dx-email.com",
    "Skype": "anthonyr_DX_skype",
    "MobilePhone": "(310) 555-6625",
    "BirthDate": "1986-02-19",
    "HireDate": "2013-01-19"
}, {
    "ID": 20,
    "HeadID": 8,
    "FullName": "Olivia Peyton",
    "Position": "Sales Assistant",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "oliviap@dx-email.com",
    "Skype": "oliviap_DX_skype",
    "MobilePhone": "(310) 555-2728",
    "BirthDate": "1981-06-03",
    "HireDate": "2012-05-14"
}, {
    "ID": 21,
    "HeadID": 6,
    "FullName": "Taylor Riley",
    "Position": "Network Admin",
    "City": "San Jose",
    "State": "California",
    "Email": "taylorr@dx-email.com",
    "Skype": "taylorr_DX_skype",
    "MobilePhone": "(310) 555-7276",
    "BirthDate": "1982-08-14",
    "HireDate": "2012-04-14"
}, {
    "ID": 22,
    "HeadID": 6,
    "FullName": "Amelia Harper",
    "Position": "Network Admin",
    "City": "Los Angeles",
    "State": "California",
    "Email": "ameliah@dx-email.com",
    "Skype": "ameliah_DX_skype",
    "MobilePhone": "(213) 555-4276",
    "BirthDate": "1983-11-19",
    "HireDate": "2011-02-10"
}, {
    "ID": 23,
    "HeadID": 6,
    "FullName": "Wally Hobbs",
    "Position": "Programmer",
    "City": "Chatsworth",
    "State": "California",
    "Email": "wallyh@dx-email.com",
    "Skype": "wallyh_DX_skype",
    "MobilePhone": "(818) 555-8872",
    "BirthDate": "1984-12-24",
    "HireDate": "2011-02-17"
}, {
    "ID": 24,
    "HeadID": 6,
    "FullName": "Brad Jameson",
    "Position": "Programmer",
    "City": "San Fernando",
    "State": "California",
    "Email": "bradleyj@dx-email.com",
    "Skype": "bradleyj_DX_skype",
    "MobilePhone": "(818) 555-4646",
    "BirthDate": "1988-10-12",
    "HireDate": "2011-03-02"
}, {
    "ID": 25,
    "HeadID": 6,
    "FullName": "Karen Goodson",
    "Position": "Programmer",
    "City": "South Pasadena",
    "State": "California",
    "Email": "kareng@dx-email.com",
    "Skype": "kareng_DX_skype",
    "MobilePhone": "(626) 555-0908",
    "BirthDate": "1987-04-26",
    "HireDate": "2011-03-14"
}, {
    "ID": 26,
    "HeadID": 5,
    "FullName": "Marcus Orbison",
    "Position": "Travel Coordinator",
    "City": "Los Angeles",
    "State": "California",
    "Email": "marcuso@dx-email.com",
    "Skype": "marcuso_DX_skype",
    "MobilePhone": "(213) 555-7098",
    "BirthDate": "1982-03-02",
    "HireDate": "2005-05-19"
}, {
    "ID": 27,
    "HeadID": 5,
    "FullName": "Sandy Bright",
    "Position": "Benefits Coordinator",
    "City": "Denver",
    "State": "Colorado",
    "Email": "sandrab@dx-email.com",
    "Skype": "sandrab_DX_skype",
    "MobilePhone": "(818) 555-0524",
    "BirthDate": "1983-09-11",
    "HireDate": "2005-06-04"
}, {
    "ID": 28,
    "HeadID": 6,
    "FullName": "Morgan Kennedy",
    "Position": "Graphic Designer",
    "City": "San Fernando Valley",
    "State": "California",
    "Email": "morgank@dx-email.com",
    "Skype": "morgank_DX_skype",
    "MobilePhone": "(818) 555-8238",
    "BirthDate": "1984-07-17",
    "HireDate": "2012-01-11"
}, {
    "ID": 29,
    "HeadID": 28,
    "FullName": "Violet Bailey",
    "Position": "Jr Graphic Designer",
    "City": "La Canada",
    "State": "California",
    "Email": "violetb@dx-email.com",
    "Skype": "violetb_DX_skype",
    "MobilePhone": "(818) 555-2478",
    "BirthDate": "1985-06-10",
    "HireDate": "2012-01-19"
}, {
    "ID": 30,
    "HeadID": 5,
    "FullName": "Ken Samuelson",
    "Position": "Ombudsman",
    "City": "St. Louis",
    "State": "Missouri",
    "Email": "kents@dx-email.com",
    "Skype": "kents_DX_skype",
    "MobilePhone": "(562) 555-9282",
    "BirthDate": "1972-09-11",
    "HireDate": "2009-04-22"
}];

export default {
    getEmployees() {
        return employees;
    }
}
React
App.js
employees.js
import React from 'react';

import 'devextreme/dist/css/dx.light.css';

import {
    TreeList
} from 'devextreme-react/tree-list';
import { employees } from './employees';

function App() {
    return (
        <div className="App">
            <TreeList
                dataSource={employees}
                rootValue={-1}
                keyExpr="ID"
                parentIdExpr="HeadID">
            </TreeList>
        </div>
    );
}

export default App;
export const employees = [{
    "ID": 1,
    "HeadID": -1,
    "FullName": "John Heart",
    "Position": "CEO",
    "City": "Los Angeles",
    "State": "California",
    "Email": "jheart@dx-email.com",
    "Skype": "jheart_DX_skype",
    "MobilePhone": "(213) 555-9392",
    "BirthDate": "1964-03-16",
    "HireDate": "1995-01-15"
}, {
    "ID": 2,
    "HeadID": 1,
    "FullName": "Samantha Bright",
    "Position": "COO",
    "City": "Los Angeles",
    "State": "California",
    "Email": "samanthab@dx-email.com",
    "Skype": "samanthab_DX_skype",
    "MobilePhone": "(213) 555-2858",
    "BirthDate": "1966-05-02",
    "HireDate": "2004-05-24"
}, {
    "ID": 3,
    "HeadID": 1,
    "FullName": "Arthur Miller",
    "Position": "CTO",
    "City": "Denver",
    "State": "Colorado",
    "Email": "arthurm@dx-email.com",
    "Skype": "arthurm_DX_skype",
    "MobilePhone": "(310) 555-8583",
    "BirthDate": "1972-07-11",
    "HireDate": "2007-12-18"
}, {
    "ID": 4,
    "HeadID": 1,
    "FullName": "Robert Reagan",
    "Position": "CMO",
    "City": "Bentonville",
    "State": "Arkansas",
    "Email": "robertr@dx-email.com",
    "Skype": "robertr_DX_skype",
    "MobilePhone": "(818) 555-2387",
    "BirthDate": "1974-09-07",
    "HireDate": "2002-11-08"
}, {
    "ID": 5,
    "HeadID": 1,
    "FullName": "Greta Sims",
    "Position": "HR Manager",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "gretas@dx-email.com",
    "Skype": "gretas_DX_skype",
    "MobilePhone": "(818) 555-6546",
    "BirthDate": "1977-11-22",
    "HireDate": "1998-04-23"
}, {
    "ID": 6,
    "HeadID": 3,
    "FullName": "Brett Wade",
    "Position": "IT Manager",
    "City": "Reno",
    "State": "Nevada",
    "Email": "brettw@dx-email.com",
    "Skype": "brettw_DX_skype",
    "MobilePhone": "(626) 555-0358",
    "BirthDate": "1968-12-01",
    "HireDate": "2009-03-06"
}, {
    "ID": 7,
    "HeadID": 5,
    "FullName": "Sandra Johnson",
    "Position": "Controller",
    "City": "Beaver",
    "State": "Utah",
    "Email": "sandraj@dx-email.com",
    "Skype": "sandraj_DX_skype",
    "MobilePhone": "(562) 555-2082",
    "BirthDate": "1974-11-15",
    "HireDate": "2005-05-11"
}, {
    "ID": 8,
    "HeadID": 4,
    "FullName": "Ed Holmes",
    "Position": "Sales Manager",
    "City": "Malibu",
    "State": "California",
    "Email": "edwardh@dx-email.com",
    "Skype": "edwardh_DX_skype",
    "MobilePhone": "(310) 555-1288",
    "BirthDate": "1973-07-14",
    "HireDate": "2005-06-19"
}, {
    "ID": 9,
    "HeadID": 3,
    "FullName": "Barb Banks",
    "Position": "Support Manager",
    "City": "Phoenix",
    "State": "Arizona",
    "Email": "barbarab@dx-email.com",
    "Skype": "barbarab_DX_skype",
    "MobilePhone": "(310) 555-3355",
    "BirthDate": "1979-04-14",
    "HireDate": "2002-08-07"
}, {
    "ID": 10,
    "HeadID": 2,
    "FullName": "Kevin Carter",
    "Position": "Shipping Manager",
    "City": "San Diego",
    "State": "California",
    "Email": "kevinc@dx-email.com",
    "Skype": "kevinc_DX_skype",
    "MobilePhone": "(213) 555-2840",
    "BirthDate": "1978-01-09",
    "HireDate": "2009-08-11"
}, {
    "ID": 11,
    "HeadID": 5,
    "FullName": "Cindy Stanwick",
    "Position": "HR Assistant",
    "City": "Little Rock",
    "State": "Arkansas",
    "Email": "cindys@dx-email.com",
    "Skype": "cindys_DX_skype",
    "MobilePhone": "(818) 555-6655",
    "BirthDate": "1985-06-05",
    "HireDate": "2008-03-24"
}, {
    "ID": 12,
    "HeadID": 8,
    "FullName": "Sammy Hill",
    "Position": "Sales Assistant",
    "City": "Pasadena",
    "State": "California",
    "Email": "sammyh@dx-email.com",
    "Skype": "sammyh_DX_skype",
    "MobilePhone": "(626) 555-7292",
    "BirthDate": "1984-02-17",
    "HireDate": "2012-02-01"
}, {
    "ID": 13,
    "HeadID": 10,
    "FullName": "Davey Jones",
    "Position": "Shipping Assistant",
    "City": "Pasadena",
    "State": "California",
    "Email": "davidj@dx-email.com",
    "Skype": "davidj_DX_skype",
    "MobilePhone": "(626) 555-0281",
    "BirthDate": "1983-03-06",
    "HireDate": "2011-04-24"
}, {
    "ID": 14,
    "HeadID": 10,
    "FullName": "Victor Norris",
    "Position": "Shipping Assistant",
    "City": "Little Rock",
    "State": "Arkansas",
    "Email": "victorn@dx-email.com",
    "Skype": "victorn_DX_skype",
    "MobilePhone": "(213) 555-9278",
    "BirthDate": "1986-07-23",
    "HireDate": "2012-07-23"
}, {
    "ID": 15,
    "HeadID": 10,
    "FullName": "Mary Stern",
    "Position": "Shipping Assistant",
    "City": "Beaver",
    "State": "Utah",
    "Email": "marys@dx-email.com",
    "Skype": "marys_DX_skype",
    "MobilePhone": "(818) 555-7857",
    "BirthDate": "1982-04-08",
    "HireDate": "2012-08-12"
}, {
    "ID": 16,
    "HeadID": 10,
    "FullName": "Robin Cosworth",
    "Position": "Shipping Assistant",
    "City": "Los Angeles",
    "State": "California",
    "Email": "robinc@dx-email.com",
    "Skype": "robinc_DX_skype",
    "MobilePhone": "(818) 555-0942",
    "BirthDate": "1981-06-12",
    "HireDate": "2012-09-01"
}, {
    "ID": 17,
    "HeadID": 9,
    "FullName": "Kelly Rodriguez",
    "Position": "Support Assistant",
    "City": "Boise",
    "State": "Idaho",
    "Email": "kellyr@dx-email.com",
    "Skype": "kellyr_DX_skype",
    "MobilePhone": "(818) 555-9248",
    "BirthDate": "1988-05-11",
    "HireDate": "2012-10-13"
}, {
    "ID": 18,
    "HeadID": 9,
    "FullName": "James Anderson",
    "Position": "Support Assistant",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "jamesa@dx-email.com",
    "Skype": "jamesa_DX_skype",
    "MobilePhone": "(323) 555-4702",
    "BirthDate": "1987-01-29",
    "HireDate": "2012-10-18"
}, {
    "ID": 19,
    "HeadID": 9,
    "FullName": "Antony Remmen",
    "Position": "Support Assistant",
    "City": "Boise",
    "State": "Idaho",
    "Email": "anthonyr@dx-email.com",
    "Skype": "anthonyr_DX_skype",
    "MobilePhone": "(310) 555-6625",
    "BirthDate": "1986-02-19",
    "HireDate": "2013-01-19"
}, {
    "ID": 20,
    "HeadID": 8,
    "FullName": "Olivia Peyton",
    "Position": "Sales Assistant",
    "City": "Atlanta",
    "State": "Georgia",
    "Email": "oliviap@dx-email.com",
    "Skype": "oliviap_DX_skype",
    "MobilePhone": "(310) 555-2728",
    "BirthDate": "1981-06-03",
    "HireDate": "2012-05-14"
}, {
    "ID": 21,
    "HeadID": 6,
    "FullName": "Taylor Riley",
    "Position": "Network Admin",
    "City": "San Jose",
    "State": "California",
    "Email": "taylorr@dx-email.com",
    "Skype": "taylorr_DX_skype",
    "MobilePhone": "(310) 555-7276",
    "BirthDate": "1982-08-14",
    "HireDate": "2012-04-14"
}, {
    "ID": 22,
    "HeadID": 6,
    "FullName": "Amelia Harper",
    "Position": "Network Admin",
    "City": "Los Angeles",
    "State": "California",
    "Email": "ameliah@dx-email.com",
    "Skype": "ameliah_DX_skype",
    "MobilePhone": "(213) 555-4276",
    "BirthDate": "1983-11-19",
    "HireDate": "2011-02-10"
}, {
    "ID": 23,
    "HeadID": 6,
    "FullName": "Wally Hobbs",
    "Position": "Programmer",
    "City": "Chatsworth",
    "State": "California",
    "Email": "wallyh@dx-email.com",
    "Skype": "wallyh_DX_skype",
    "MobilePhone": "(818) 555-8872",
    "BirthDate": "1984-12-24",
    "HireDate": "2011-02-17"
}, {
    "ID": 24,
    "HeadID": 6,
    "FullName": "Brad Jameson",
    "Position": "Programmer",
    "City": "San Fernando",
    "State": "California",
    "Email": "bradleyj@dx-email.com",
    "Skype": "bradleyj_DX_skype",
    "MobilePhone": "(818) 555-4646",
    "BirthDate": "1988-10-12",
    "HireDate": "2011-03-02"
}, {
    "ID": 25,
    "HeadID": 6,
    "FullName": "Karen Goodson",
    "Position": "Programmer",
    "City": "South Pasadena",
    "State": "California",
    "Email": "kareng@dx-email.com",
    "Skype": "kareng_DX_skype",
    "MobilePhone": "(626) 555-0908",
    "BirthDate": "1987-04-26",
    "HireDate": "2011-03-14"
}, {
    "ID": 26,
    "HeadID": 5,
    "FullName": "Marcus Orbison",
    "Position": "Travel Coordinator",
    "City": "Los Angeles",
    "State": "California",
    "Email": "marcuso@dx-email.com",
    "Skype": "marcuso_DX_skype",
    "MobilePhone": "(213) 555-7098",
    "BirthDate": "1982-03-02",
    "HireDate": "2005-05-19"
}, {
    "ID": 27,
    "HeadID": 5,
    "FullName": "Sandy Bright",
    "Position": "Benefits Coordinator",
    "City": "Denver",
    "State": "Colorado",
    "Email": "sandrab@dx-email.com",
    "Skype": "sandrab_DX_skype",
    "MobilePhone": "(818) 555-0524",
    "BirthDate": "1983-09-11",
    "HireDate": "2005-06-04"
}, {
    "ID": 28,
    "HeadID": 6,
    "FullName": "Morgan Kennedy",
    "Position": "Graphic Designer",
    "City": "San Fernando Valley",
    "State": "California",
    "Email": "morgank@dx-email.com",
    "Skype": "morgank_DX_skype",
    "MobilePhone": "(818) 555-8238",
    "BirthDate": "1984-07-17",
    "HireDate": "2012-01-11"
}, {
    "ID": 29,
    "HeadID": 28,
    "FullName": "Violet Bailey",
    "Position": "Jr Graphic Designer",
    "City": "La Canada",
    "State": "California",
    "Email": "violetb@dx-email.com",
    "Skype": "violetb_DX_skype",
    "MobilePhone": "(818) 555-2478",
    "BirthDate": "1985-06-10",
    "HireDate": "2012-01-19"
}, {
    "ID": 30,
    "HeadID": 5,
    "FullName": "Ken Samuelson",
    "Position": "Ombudsman",
    "City": "St. Louis",
    "State": "Missouri",
    "Email": "kents@dx-email.com",
    "Skype": "kents_DX_skype",
    "MobilePhone": "(562) 555-9282",
    "BirthDate": "1972-09-11",
    "HireDate": "2009-04-22"
}];

If you run this code, a TreeList is created with a column for each data field. All the columns have equal widths and the same order as their data fields.

Expand Rows

You can use the expandedRowKeys property to expand individual rows. Alternatively, you can enable the autoExpandAll property to expand all rows, as shown in the following code:

jQuery
index.js
$(function() {
    $("#treeList").dxTreeList({
        // ...
        autoExpandAll: true
    });
});
Angular
app.component.html
<dx-tree-list ... 
    [autoExpandAll]="true">
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ... 
            :auto-expand-all="true">
            <!-- ... -->
        </DxTreeList>
    </div>
</template>

<script>
// ...
</script>
React
App.js
// ...

function App() {
    return (
        <div className="App">
            <TreeList ... 
                autoExpandAll={true}>
                {/* ... */}
            </TreeList>
        </div>
    );
}

export default App;

Customize Columns

Declare the columns array to customize columns. This array can contain objects (column configurations) and text strings (data field names). You can use text strings if you do not need to specify column properties except the dataField.

Reorder Columns

To reorder columns, change their order in the columns array. Users can also reorder columns if you enable the allowColumnReordering property.

jQuery
index.js
$(function() {
    $("#treeList").dxTreeList({
        // ...
        columns: [ "FullName", "Position", {
            dataField: "BirthDate", 
            dataType: "date",
        }, {
            dataField: "HireDate", 
            dataType: "date",
        }, "City", "State", "Email", "MobilePhone", "Skype"],
        allowColumnReordering: true
    });
});
Angular
app.component.html
<dx-tree-list ...
    [allowColumnReordering]="true">
    <dxi-column dataField="FullName"></dxi-column>
    <dxi-column dataField="Position"></dxi-column>
    <dxi-column
        dataField="BirthDate"
        dataType="date">
    </dxi-column>
    <dxi-column
        dataField="HireDate"
        dataType="date">
    </dxi-column>
    <dxi-column dataField="City"></dxi-column>
    <dxi-column dataField="State"></dxi-column>
    <dxi-column dataField="Email"></dxi-column>
    <dxi-column dataField="MobilePhone"></dxi-column>
    <dxi-column dataField="Skype"></dxi-column>
</dx-tree-list>
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ...
            :allow-column-reordering="true">
            <DxColumn data-field="FullName"" />
            <DxColumn data-field="Position" />
            <DxColumn
                data-field="BirthDate"
                data-type="date">
            </DxColumn>
            <DxColumn
                data-field="HireDate"
                data-type="date">
            </DxColumn>
            <DxColumn data-field="City" />
            <DxColumn data-field="State" />
            <DxColumn data-field="Email" />
            <DxColumn data-field="MobilePhone" />
            <DxColumn data-field="Skype" />
        </DxTreeList>
    </div>
</template>

<script>
// ...
import {
    DxTreeList,
    DxColumn
} from 'devextreme-vue/tree-list';

export default {
    components: {
        DxTreeList,
        DxColumn
    },
    // ...
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.light.css';

import {
    TreeList,
    Column
} from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList ...
                allowColumnReordering={true}>
                <Column dataField="FullName" />
                <Column dataField="Position" />
                <Column
                    dataField="BirthDate"
                    dataType="date">
                </Column>
                <Column
                    dataField="HireDate"
                    dataType="date">
                </Column>
                <Column dataField="City" />
                <Column dataField="State" />
                <Column dataField="Email" />
                <Column dataField="MobilePhone" />
                <Column dataField="Skype" />
            </TreeList>
        </div>
    );
}

export default App;

In the code above, we also specify the "date" dataType for the BirthDate and HireDate columns because BirthDate and HireDate are stored as strings, but you want the TreeList to treat them as date-time values.

Resize Columns

TreeList columns have equal widths (the default setting). You can set each column's width or indicate that all columns should adjust their widths to their content (columnAutoWidth). Users can resize columns if you enable the allowColumnResizing property.

jQuery
index.js
$(function() {
    $("#treeList").dxTreeList({
        // ...
        columns: [
        // ...
        {
            dataField: "BirthDate", 
            dataType: "date",
            width: 100,
        }, {
            dataField: "HireDate", 
            dataType: "date",
            width: 100,
        },
        // ...
        ],
        allowColumnResizing: true,
        columnAutoWidth: true,
    });
});
Angular
app.component.html
<dx-tree-list ...
    [allowColumnResizing]="true"
    [columnAutoWidth]="true">
    <!-- ... -->
    <dxi-column
        dataField="BirthDate"
        dataType="date"
        [width]="100">
    </dxi-column>
    <dxi-column
        dataField="HireDate"
        dataType="date"
        [width]="100">
    </dxi-column>
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ...
            :allow-column-resizing="true"
            :column-auto-width="true">
            <!-- ... -->
            <DxColumn
                data-field="BirthDate"
                data-type="date"
                :width="100">
            </DxColumn>
            <DxColumn
                data-field="HireDate"
                data-type="date"
                :width="100">
            </DxColumn>
            <!-- ... -->
        </DxTreeList>
    </div>
</template>

<script>
import {
    DxTreeList,
    DxColumn
} from 'devextreme-vue/tree-list';

export default {
    components: {
        DxTreeList,
        DxColumn
    },
    // ...
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.light.css';

import {
    TreeList,
    Column
} from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList ...
                columnAutoWidth={true}
                allowColumnResizing={true}>
                {/* ... */}
                <Column
                    dataField="BirthDate"
                    dataType="date"
                    width={100}>
                </Column>
                <Column
                    dataField="HireDate"
                    dataType="date"
                    width={100}>
                </Column>
                {/* ... */}
            </TreeList>
        </div>
    );
}

export default App;

Fix Columns

When the width of all columns exceeds the UI component's width, users can scroll the TreeList horizontally. If you set the columnFixing.enabled property to true, users can use a column header's context menu to show certain columns in the view regardless of how far they scroll the TreeList.

You can also enable a column's fixed property in code. This fixes the column to the UI component's left edge. To change the position, set the fixedPosition property.

The following code fixes the FullName column to the default position and allows users to fix and unfix columns at runtime:

jQuery
index.js
$(function() {
    $("#treeList").dxTreeList({
        // ...
        columnFixing: { enabled: true },
        columns: [{
            dataField: "FullName", 
            fixed: true
        },
        // ...
        ],
        // ...
    });
});
Angular
app.component.html
<dx-tree-list ... >
    <dxi-column
        dataField="FullName"
        [fixed]="true">
    </dxi-column>
    <!-- ... -->
    <dxo-column-fixing [enabled]="true"></dxo-column-fixing>
</dx-tree-list>
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ... >
            <DxColumn
                data-field="FullName"
                :fixed="true">
            </DxColumn>
            <!-- ... -->
            <DxColumnFixing :enabled="true" />
        </DxTreeList>
    </div>
</template>

<script>
import {
    DxTreeList,
    DxColumn,
    DxColumnFixing
} from 'devextreme-vue/tree-list';

export default {
    components: {
        DxTreeList,
        DxColumn,
        DxColumnFixing
    },
    // ...
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.light.css';

import {
    TreeList,
    Column,
    ColumnFixing
} from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList ... >
                {/* ... */}
                <Column
                    dataField="FullName"
                    fixed={true}>
                </Column>
                {/* ... */}
                <ColumnFixing enabled={true} />
            </TreeList>
        </div>
    );
}

export default App;

Hide Columns

The TreeList displays all columns from the columns array. To hide a column, set its visible property to false. Hidden columns appear in the columnChooser. Users can restore hidden columns from it. To enable the column chooser, set the columnChooser.enabled property to true. If a column should not be visible in the column chooser, do not declare it in the columns array.

jQuery
index.js
$(function() {
    $("#treeList").dxTreeList({
        // ...
        columns: [
        // ...
        {
            dataField: "Email",
            visible: false
        }],
        columnChooser: { enabled: true },
    });
});
Angular
app.component.html
<dx-tree-list ... >
    <!-- ... -->
    <dxi-column dataField="Email" [visible]="false"></dxi-column>
    <dxo-column-chooser [enabled]="true"></dxo-column-chooser>
</dx-tree-list>
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ... >
            <!-- ... -->
            <DxColumn data-field="Email" :visible="false" />
            <DxColumnChooser :enabled="true" />
        </DxTreeList>
    </div>
</template>

<script>
import {
    DxTreeList,
    DxColumn,
    DxColumnChooser
} from 'devextreme-vue/tree-list';

export default {
    components: {
        DxTreeList,
        DxColumn,
        DxColumnChooser
    },
    // ...
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.light.css';

import {
    TreeList,
    Column,
    ColumnChooser
} from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList ... >
                {/* ... */}
                <Column dataField="Email" visible={false} />
                <ColumnChooser enabled={true} />
            </TreeList>
        </div>
    );
}

export default App;

Sort Data

The sorting.mode property specifies whether users can sort records by single or multiple columns. This tutorial uses the default sorting mode - single.

You can also set a column's sortOrder and sortIndex properties to specify the initial sort settings. sortIndex applies only in multiple sorting mode.

jQuery
index.js
$(function() {
    $("#treeList").dxTreeList({
        // ...
        columns: [{
            dataField: "State",
            sortOrder: "asc",
        },
        // ...
        ],
        // sorting: { mode: "single" },
    });
});
Angular
app.component.html
<dx-tree-list ... >
    <!-- ... -->
    <dxi-column
        dataField="State"
        sortOrder="asc">
    </dxi-column>
    <!-- <dxo-sorting [mode]="single"></dxo-sorting> -->
</dx-tree-list>
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ... >
            <!-- ... -->
            <DxColumn
                data-field="State"
                sort-order="asc">
            </DxColumn>
            <!-- <DxSorting mode="single" /> -->
        </DxTreeList>
    </div>
</template>

<script>
import {
    DxTreeList,
    DxColumn,
    // ...
    // DxSorting
} from 'devextreme-vue/tree-list';

export default {
    components: {
        DxTreeList,
        DxColumn,
        // ...
        // DxSorting
    },
    // ...
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.light.css';

import {
    TreeList,
    Column,
    // ...
    // Sorting
} from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList ... >
                {/* ... */}
                <Column
                    dataField="State"
                    sortOrder="asc">
                </Column>
                {/* <Sorting mode="single" /> */}
            </TreeList>
        </div>
    );
}

export default App;

Filter and Search Data

The TreeList includes the following UI elements used to filter and search data:

In this tutorial, the filterRow and searchPanel are displayed:

jQuery
index.js
$(function() {
    $("#treeList").dxTreeList({
        // ...
        filterRow: { visible: true },
        searchPanel: { visible: true },
    });
});
Angular
app.component.html
<dx-tree-list ... >
    <!-- ... -->
    <dxo-filter-row [visible]="true"></dxo-filter-row>
    <dxo-search-panel [visible]="true"></dxo-search-panel>
</dx-tree-list>
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ... >
            <!-- ... -->
            <DxFilterRow :visible="true" />
            <DxSearchPanel :visible="true" />
        </DxTreeList>
    </div>
</template>

<script>
import {
    DxTreeList,
    // ...
    DxFilterRow,
    DxSearchPanel
} from 'devextreme-vue/tree-list';

export default {
    components: {
        DxTreeList,
        // ...
        DxFilterRow,
        DxSearchPanel
    },
    // ...
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.light.css';

import {
    TreeList,
    // ...
    FilterRow,
    SearchPanel
} from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList ... >
                {/* ... */}
                <FilterRow visible={true} />
                <SearchPanel visible={true} />
            </TreeList>
        </div>
    );
}

export default App;

Run the code and enter a value in a filter row cell or the search panel. Data is filtered according to this value.

Edit and Validate Data

Users can add, update, and delete records. To allow these operations, enable the allowAdding, allowUpdating, and allowDeleting properties in the editing object. Multiple edit modes are available. This tutorial uses the pop-up edit mode.

DevExtreme includes a validation engine that checks edited values before they are saved. This engine supports different validation rule types, such as Email, Compare, Range, and more. Validation rules are specified per column; one column can use multiple rules. The code below assigns the Required rule to several columns.

jQuery
index.js
$(function() {
    $("#treeList").dxTreeList({
        editing: {
            mode: "popup",
            allowUpdating: true,
            allowDeleting: true,
            allowAdding: true
        },
        columns: [{
            dataField: "FullName",
            validationRules: [{ type: "required" }]
        }, {
            dataField: "Position",
            validationRules: [{ type: "required" }]
        }, {
            dataField: "BirthDate",
            // ...
            validationRules: [{ type: "required" }]
        }, {
            dataField: "HireDate", 
            // ...
            validationRules: [{ type: "required" }]
        },
        // ...
        {
            dataField: "State",
            // ...
            validationRules: [{ type: "required" }],
        }
        // ...
    );
});
Angular
app.component.html
<dx-tree-list ... >
    <!-- ... -->
    <dxi-column dataField="FullName">
        <dxi-validation-rule type="required"></dxi-validation-rule>
    </dxi-column>
    <dxi-column dataField="Position">
        <dxi-validation-rule type="required"></dxi-validation-rule>
    </dxi-column>
    <dxi-column ...
        dataField="BirthDate">
        <dxi-validation-rule type="required"></dxi-validation-rule>
    </dxi-column>
    <dxi-column ...
        dataField="HireDate">
        <dxi-validation-rule type="required"></dxi-validation-rule>
    </dxi-column>
    <dxi-column ...
        dataField="State">
        <dxi-validation-rule type="required"></dxi-validation-rule>
    </dxi-column>
    <dxo-editing
        mode="popup"
        [allowUpdating]="true"
        [allowDeleting]="true"
        [allowAdding]="true">
    </dxo-editing>
</dx-tree-list>
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ... >
            <!-- ... -->
            <DxColumn data-field="FullName">
                <DxRequiredRule />
            </DxColumn>
            <DxColumn data-field="Position">
                <DxRequiredRule />
            </DxColumn>
            <DxColumn ...
                data-field="BirthDate">
                <DxRequiredRule />
            </DxColumn>
            <DxColumn ...
                data-field="HireDate">
                <DxRequiredRule />
            </DxColumn>
            <!-- ... -->
            <DxColumn ...
                data-field="State">
                <DxRequiredRule />
            </DxColumn>
            <DxEditing
                mode="popup"
                :allow-updating="true"
                :allow-adding="true"
                :allow-deleting="true"
            />
        </DxTreeList>
    </div>
</template>

<script>
import {
    DxTreeList,
    DxColumn,
    // ...
    DxRequiredRule,
    DxEditing
} from 'devextreme-vue/tree-list';

export default {
    components: {
        DxTreeList,
        DxColumn,
        // ...
        DxRequiredRule,
        DxEditing
    },
    // ...
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.light.css';

import {
    TreeList,
    Column,
    // ...
    RequiredRule,
    Editing
} from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList ... >
                {/* ... */}
                <Column dataField="FullName">
                    <RequiredRule />
                </Column>
                <Column dataField="Position">
                    <RequiredRule />
                </Column>
                {/* ... */}
                <Column ...
                    dataField="HireDate">
                    <RequiredRule />
                </Column>
                {/* ... */}
                <Column ...
                    dataField="State">
                    <RequiredRule />
                </Column>
                <Editing
                    mode="popup"
                    allowUpdating={true}
                    allowDeleting={true}
                    allowAdding={true}
                />
            </TreeList>
        </div>
    );
}

export default App;

Run the code and click the Edit button in any row. This invokes a pop-up window that contains an edit form. When you clear the Full Name text box on this form, the following validation error is shown: "Full Name is required".

NOTE
Editing does not work with hierarchical data sources out of the box, but you can use the code sample from the following knowledge base article to implement it: TreeList - How to perform CRUD operations on a hierarchical data source.

Select Records

The TreeList supports single and multiple record selection modes. Use the selection.mode property to specify the mode.

You can obtain the selected record's data in the onSelectionChanged function. In the code below, this function displays the selected employee under the TreeList:

jQuery
index.js
index.html
index.css
$(function() {
    $("#treeList").dxTreeList({
        // ...
        selection: { mode: "single" },
        onSelectionChanged: function(e) {
            e.component.byKey(e.currentSelectedRowKeys[0]).done(employee => {
                if(employee) {
                    $("#selected-employee").text(`Selected employee: ${employee.FullName}`);
                }
            });
        },
    });
});
<html>
    <!-- ... -->
    <body class="dx-viewport">
        <div id="app-container">
            <div id="treeList"></div>
            <p id="selected-employee"></p>
        </div>
    </body>
</html>
/* ... */
#app-container {
    width: 900px;
    position: relative;
}

#selected-employee {
    position: absolute;
    left: 50%;
    transform: translate(-50%, 0);
}
Angular
app.component.html
app.component.ts
app.component.css
<div id="app-container">
    <dx-tree-list ...
        (onSelectionChanged)="selectEmployee($event)">
        <!-- ... -->
        <dxo-selection mode="single"></dxo-selection>
    </dx-tree-list>
    <p id="selected-employee" *ngIf="selectedEmployee">
        Selected employee: {{ selectedEmployee.FullName }}
    </p>
</div>
import { Component } from '@angular/core';
import { Employee, EmployeesService } from './employees.service';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    // ...
    selectedEmployee: Employee;

    constructor(service: EmployeesService) {
        // ...
        this.selectEmployee = this.selectEmployee.bind(this);
    }

    selectEmployee(e) {
        e.component.byKey(e.currentSelectedRowKeys[0]).done(employee => {
            if(employee) {
                this.selectedEmployee = employee;
            }
        });
    }
}
/* ... */
#app-container {
    width: 900px;
    position: relative;
}

#selected-employee {
    position: absolute;
    left: 50%;
    transform: translate(-50%, 0);
}
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ...
            @selection-changed="selectEmployee">
            <!-- ... -->
            <DxSelection mode="single" />
        </DxTreeList>
        <p id="selected-employee" v-if="selectedEmployee">
            Selected employee: {{ selectedEmployee.FullName }}
        </p>
    </div>
</template>

<script>
import {
    DxTreeList,
    // ...
    DxSelection
} from 'devextreme-vue/tree-list';

export default {
    components: {
        DxTreeList,
        // ...
        DxSelection
    },
    data() {
        return {
            // ...
            selectedEmployee: undefined,
        }
    },
    methods: {
        selectEmployee(e) {
            e.component.byKey(e.currentSelectedRowKeys[0]).done(employee => {
                if(employee) {
                    this.selectedEmployee = employee;
                }
            });
        }
    }
}
</script>

<style>
/* ... */
#app-container {
    width: 900px;
    position: relative;
}

#selected-employee {
    position: absolute;
    left: 50%;
    transform: translate(-50%, 0);
}
</style>
React
App.js
App.css
import React, { useCallback, useState } from 'react';
import 'devextreme/dist/css/dx.light.css';
import './App.css';

import {
    TreeList,
    Column,
    // ...
    Selection
} from 'devextreme-react/tree-list';

function SelectedEmployee(props) {
    if(props.employee) {
        return (
            <p id="selected-employee">
                Selected employee: {props.employee.FullName}
            </p>
        );
    }
    return null;
}

function App() {
    const [selectedEmployee, setSelectedEmployee] = useState();
    const selectEmployee = useCallback((e) => {
        e.component.byKey(e.currentSelectedRowKeys[0]).done(employee => {
            setSelectedEmployee(employee);
        });
    }, []);

    return (
        <div className="App">
            <TreeList ...
                onSelectionChanged={selectEmployee}>
                {/* ... */}
                <Selection mode="single" />
            </TreeList>
            <SelectedEmployee employee={selectedEmployee} />
        </div>
    );
}

export default App;
/* ... */
.App {
    width: 900px;
    position: relative;
}

#selected-employee {
    position: absolute;
    left: 50%;
    transform: translate(-50%, 0);
}

Customize the Toolbar

The TreeList includes an integrated toolbar that displays predefined and custom controls. To add or remove toolbar items, declare the toolbar.items[] array. Toolbar items within the UI preserve the order in which they are declared.

This tutorial illustrates how to add the following items to the toolbar:

  • Predefined controls
    Declare a toolbar item element and specify the name and properties that you want to customize (see the "addRowButton" configuration in the code below). If a control does not need customization, include its name only. Ensure that items[] contain controls for all features that you enabled in your TreeList.

  • DevExtreme components
    Configure a DevExtreme component within a toolbar item element. In this tutorial, we extended the toolbar's item collection with a custom Button that expands or collapses all grid records.

jQuery
index.js
$(function() {
    const treeList = $("#treeList").dxTreeList({
        // ...
        toolbar: {
            items: [
                {
                    location: "after",
                    widget: "dxButton",
                    options: {
                        text: "Collapse All",
                        width: 136,
                        onClick(e) {
                            const expanding = e.component.option("text") === "Expand All";
                            treeList.option({
                                autoExpandAll: expanding,
                                expandedRowKeys: []
                            });
                            e.component.option("text", expanding ? "Collapse All" : "Expand All");
                        },
                    },
                },
                {
                    name: "addRowButton",
                    showText: "always"
                },
                "exportButton",
                "columnChooserButton",
                "searchPanel"
            ]
        },
    }).dxTreeList("instance");
});
Angular
app.component.html
app.component.ts
<div id="app-container">
    <dx-tree-list ...
        [autoExpandAll]="expanded"
        [(expandedRowKeys)]="expandedRowKeys">
        <!-- ... -->
        <dxo-toolbar>
            <dxi-item location="after">
                <dx-button
                    [text]="expanded ? 'Collapse All' : 'Expand All'"
                    [width]="136"
                    (onClick)="expanded = !expanded; expandedRowKeys = []">
                </dx-button>
            </dxi-item>
            <dxi-item name="addRowButton" showText="always"></dxi-item>
            <dxi-item name="exportButton"></dxi-item>
            <dxi-item name="columnChooserButton"></dxi-item>
            <dxi-item name="searchPanel"></dxi-item>
        </dxo-toolbar>
    </dx-tree-list>
</div>
// ...
@Component({
    // ...
})
export class AppComponent {
    // ...
    expanded: Boolean = true;
    expandedRowKeys: Number[] = [];
}
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ...
            :auto-expand-all="expanded"
            :expanded-row-keys="expandedRowKeys">
            <!-- ... -->
            <DxToolbar>
                <DxItem location="after" template="button-template" />
                <DxItem name="addRowButton" show-text="always" />
                <DxItem name="exportButton" />
                <DxItem name="columnChooserButton" />
                <DxItem name="searchPanel" />
            </DxToolbar>
            <template #button-template>
                <DxButton
                    :text="expanded ? 'Collapse All' : 'Expand All'"
                    :width="136"
                    @click="expanded = !expanded; expandedRowKeys = []"
                />
            </template>
        </DxTreeList>
    </div>
</template>

<script>
import {
    DxTreeList,
    // ...
    DxToolbar,
    DxItem,
} from 'devextreme-vue/tree-list';
import { DxButton } from 'devextreme-vue/button';

export default {
    components: {
        DxTreeList,
        // ...
        DxToolbar,
        DxItem,
        DxButton
    },
    data() {
        return {
            // ...
            expanded: true,
            expandedRowKeys: []
        }
    },
    // ...
}
</script>

<style>
/* ... */
</style>
React
App.js
import React, { useCallback, useState } from 'react';
import 'devextreme/dist/css/dx.light.css';
import './App.css';

import {
    TreeList,
    // ...
    Toolbar,
    Item,
} from 'devextreme-react/tree-list';
import { Button } from 'devextreme-react/button';

// ...

function App() {
    // ...
    const [expanded, setExpanded] = useState(true);
    const [expandedRowKeys, setExpandedRowKeys] = useState([]);

    const onOptionChanged = useCallback((e) => {
        if(e.name === 'expandedRowKeys') {
            setExpandedRowKeys(e.value);
        }
    }, []);

    return (
        <div className="App">
            <TreeList ...
                autoExpandAll={expanded}
                expandedRowKeys={expandedRowKeys}
                onOptionChanged={onOptionChanged}>
                {/* ... */}
                <Toolbar>
                    <Item location="after">
                        <Button
                            text={expanded ? 'Collapse All' : 'Expand All'}
                            width={136}
                            onClick={() => {
                                setExpanded(prevExpanded => !prevExpanded)
                                setExpandedRowKeys([]);
                            }}
                        />
                    </Item>
                    <Item name="addRowButton" showText="always" />
                    <Item name="exportButton" />
                    <Item name="columnChooserButton" />
                    <Item name="searchPanel" />
                </Toolbar>
            </TreeList>
        </div>
    );
}

export default App;

Enable Row Drag & Drop

Users can drag and drop nodes to reorder them or change their hierarchy. To configure these features, enable the allowReordering and allowDropInsideItem properties of the rowDragging object and implement the onDragChange and onReorder functions as shown below. For detailed instructions, refer to the Node Drag & Drop demo.

jQuery
index.js
$(function() {
    const treeList = $("#treeList").dxTreeList({
        // ...
        rowDragging: {
            allowDropInsideItem: true,
            allowReordering: true,
            onDragChange: function(e) {
                var visibleRows = treeList.getVisibleRows(),
                    sourceNode = treeList.getNodeByKey(e.itemData.ID),
                    targetNode = visibleRows[e.toIndex].node;

                while (targetNode && targetNode.data) {
                    if (targetNode.data.ID === sourceNode.data.ID) {
                        e.cancel = true;
                        break;
                    }
                    targetNode = targetNode.parent;
                }
            },
            onReorder: function(e) {
                var visibleRows = e.component.getVisibleRows(),
                    sourceData = e.itemData,
                    targetData = visibleRows[e.toIndex].data;

                if (e.dropInsideItem) {
                    e.itemData.HeadID = targetData.ID;
                } else {
                    var sourceIndex = employees.indexOf(sourceData),
                        targetIndex = employees.indexOf(targetData);

                    if (sourceData.HeadID !== targetData.HeadID) {
                        sourceData.HeadID = targetData.HeadID;
                        if (e.toIndex > e.fromIndex) {
                            targetIndex++;
                        }
                    }
                    employees.splice(sourceIndex, 1);
                    employees.splice(targetIndex, 0, sourceData);
                }
                e.component.refresh();
            }
        },
    }).dxTreeList("instance");
});
Angular
app.component.html
app.component.ts
<div id="app-container">
    <dx-tree-list ...
        <!-- ... -->
        <dxo-row-dragging
            [onDragChange]="onDragChange"
            [onReorder]="onReorder"
            [allowDropInsideItem]="true"
            [allowReordering]="true"
        ></dxo-row-dragging>
    </dx-tree-list>
</div>
// ...

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    // ...
    selectedEmployee: Employee;

    constructor(service: EmployeesService) {
        this.employees = service.getEmployees();
        // ...
        this.onReorder = this.onReorder.bind(this);
    }

    onDragChange(e) {
        let visibleRows = e.component.getVisibleRows(),
          sourceNode = e.component.getNodeByKey(e.itemData.ID),
          targetNode = visibleRows[e.toIndex].node;

        while(targetNode && targetNode.data) {
            if (targetNode.data.ID === sourceNode.data.ID) {
                e.cancel = true;
                break;
            }
            targetNode = targetNode.parent;
        }
    }

    onReorder(e) {
        let visibleRows =  e.component.getVisibleRows(),
          sourceData = e.itemData,
          targetData = visibleRows[e.toIndex].data;

        if (e.dropInsideItem) {
            e.itemData.HeadID = targetData.ID;
            e.component.refresh();
        } else {
            let sourceIndex = this.employees.indexOf(sourceData),
              targetIndex = this.employees.indexOf(targetData);

            if (sourceData.HeadID !== targetData.HeadID) {
                sourceData.HeadID = targetData.HeadID;
                if (e.toIndex > e.fromIndex) {
                    targetIndex++;
                }
            }

            this.employees.splice(sourceIndex, 1);
            this.employees.splice(targetIndex, 0, sourceData);
        }
    }
}
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ... >
            <!-- ... -->
            <DxRowDragging
                :on-drag-change="onDragChange"
                :on-reorder="onReorder"
                :allow-drop-inside-item="true"
                :allow-reordering="true"
            />
        </DxTreeList>
    </div>
</template>

<script>
import {
    DxTreeList,
    // ...
    DxRowDragging
} from 'devextreme-vue/tree-list';

export default {
    components: {
        // ...
        DxRowDragging
    },
    data() {
        return {
            employees: service.getEmployees(),
            // ...
        }
    },
    methods: {
        // ...
        onDragChange(e) {
            let visibleRows = e.component.getVisibleRows(),
              sourceNode = e.component.getNodeByKey(e.itemData.ID),
              targetNode = visibleRows[e.toIndex].node;

            while (targetNode && targetNode.data) {
                if (targetNode.data.ID === sourceNode.data.ID) {
                    e.cancel = true;
                    break;
                }
                targetNode = targetNode.parent;
            }
        },
        onReorder(e) {
            let visibleRows = e.component.getVisibleRows(),
              sourceData = e.itemData,
              targetData = visibleRows[e.toIndex].data;

            if (e.dropInsideItem) {
                e.itemData.HeadID = targetData.ID;
                e.component.refresh();
            } else {
                let sourceIndex = this.employees.indexOf(sourceData),
                  targetIndex = this.employees.indexOf(targetData);

                if (sourceData.HeadID !== targetData.HeadID) {
                    sourceData.HeadID = targetData.HeadID;
                    if (e.toIndex > e.fromIndex) {
                        targetIndex++;
                    }
                }
                this.employees.splice(sourceIndex, 1);
                this.employees.splice(targetIndex, 0, sourceData);
                this.employees = this.employees.slice();
            }
        }
    }
}
</script>

// ...
React
App.js
import React, { useCallback, useState } from 'react';
import 'devextreme/dist/css/dx.light.css';
import './App.css';

import {
    // ...
    RowDragging
} from 'devextreme-react/tree-list';

// ...

function App() {
    // ...
    const [currentEmployees, setCurrentEmployees] = useState(employees);
    // ...

    const onDragChange = useCallback((e) => {
        let visibleRows = e.component.getVisibleRows(),
          sourceNode = e.component.getNodeByKey(e.itemData.ID),
          targetNode = visibleRows[e.toIndex].node;

        while (targetNode && targetNode.data) {
            if (targetNode.data.ID === sourceNode.data.ID) {
                e.cancel = true;
                break;
            }
            targetNode = targetNode.parent;
        }
    }, []);

    const onReorder = useCallback((e) => {
        let visibleRows = e.component.getVisibleRows(),
        sourceData = e.itemData,
        targetData = visibleRows[e.toIndex].data,
        employeesReordered = currentEmployees,
        sourceIndex = employeesReordered.indexOf(sourceData),
        targetIndex = employeesReordered.indexOf(targetData);

        if (e.dropInsideItem) {
            sourceData = { ...sourceData, HeadID: targetData.ID };
            employeesReordered = [...employeesReordered.slice(0, sourceIndex), sourceData, ...employeesReordered.slice(sourceIndex + 1)];
        } else {
            if (sourceData.HeadID !== targetData.HeadID) {
                sourceData = { ...sourceData, HeadID: targetData.HeadID };
                if (e.toIndex > e.fromIndex) {
                    targetIndex++;
                }
            }
            employeesReordered = [...employeesReordered.slice(0, sourceIndex), ...employeesReordered.slice(sourceIndex + 1)];
            employeesReordered = [...employeesReordered.slice(0, targetIndex), sourceData, ...employeesReordered.slice(targetIndex)];
        }

        setCurrentEmployees(employeesReordered);
    }, []);

    return (
        <div className="App">
            <TreeList ... >
                {/* ... */}
                <RowDragging
                    onDragChange={onDragChange}
                    onReorder={onReorder}
                    allowDropInsideItem={true}
                    allowReordering={true}
                />
            </TreeList>
            <SelectedEmployee employee={selectedEmployee} />
        </div>
    );
}

export default App;

Enable Pagination

When pagination is enabled, TreeList loads records page by page instead of loading them all at once. To configure pagination, set the paging.enabled property to true and use the paging.pageSize property to specify the optimal number of records per page. Use this feature if your tests show noticeable lags without it.

jQuery
index.js
$(function() {
    $("#treeList").dxTreeList({
        // ...
        paging: {
            enabled: true,
            pageSize: 10
        }   
    });
});
Angular
app.component.html
<dx-tree-list ... >
    <!-- ... -->
    <dxo-paging 
        [enabled]="true"
        [pageSize]="10">
    </dxo-paging>
</dx-tree-list>
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList ... >
            <!-- ... -->
            <DxPaging
                :enabled="true"
                :page-size="10"
            />
        </DxTreeList>
    </div>
</template>

<script>
import {
    // ...
    DxPaging
} from 'devextreme-vue/tree-list';

export default {
    components: {
        // ...
        DxPaging
    },
    // ...
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.light.css';

import {
    // ...
    Paging
} from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList ... >
                {/* ... */}
                <Paging
                    enabled={true}
                    defaultPageSize={10} 
                />
            </TreeList>
        </div>
    );
}

export default App;

For further information on the TreeList component, refer to the following resources: