Angular TreeList - Getting Started

DevExtreme includes multiple data management components. You can use them to display tabular, hierarchical, multidimensional data, or present information as cards. For more information about these components and their features, see How to Choose a Data Management Component.

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.

DevExtreme TreeList displays data in a multi-column tree view. The component supports local and remote data stores and allows users to shape data using sorting, filtering, and other operations.

NOTE

Need to create printable documents simply? Try our .NET-based DevExpress Reports: they ship with an intuitive Visual Studio Report Designer, Web Report Designer for end-user ad-hoc reporting, and a rich set of report controls, including cross tabs and charts.

You can generate a variety of report types — from simple mail-merge, table, and vertical reports to master-detail (hierarchical) and cross-tab reports, print or export them to PDF, Excel, and other formats.

Develop using VS Code? Leverage the capabilities of a brand new VS Code Report Designer extension to create and edit reports/documents on any platform, be it Windows, macOS, or Linux.

Get Started with DevExpress Reports | Explore Demos

Use our DevExpress BI Dashboard to embed interactive business intelligence into your next web app.

The Web Dashboard is a data analysis UI component that you can embed into your ASP.NET Core or Angular, React, and Vue applications with .NET backend. Dashboards allow you to display multiple inter-connected data analysis elements such as grids, charts, maps, gauges, and others: all within an automatically-arranged layout.

The set of components allows you to deploy an all-in-one solution and switch between Viewer and Designer modes directly on the web client (includes adaptive layouts for tablet & mobile).

The Web Dashboard is available as a part of a Universal subscription.

Get Started with DevExpress BI Dashboard | Explore Demos

This tutorial adds DevExtreme TreeList to a page, binds the component to data, and configures core TreeList features.

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

View on GitHub

Create a TreeList

jQuery

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

Angular

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

Vue

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

React

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

jQuery
index.js
index.html
$(function() {
    $("#tree-list").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/26.1.3/css/dx.light.css">
        <link rel="stylesheet" href="index.css">

        <script type="text/javascript" src="https://cdn3.devexpress.com/jslib/26.1.3/js/dx.all.js"></script>
        <script type="text/javascript" src="index.js"></script>
    </head>
    <body class="dx-viewport">
        <div id="tree-list"></div>
    </body>
</html>
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList()
    .ID("tree-list")
)
Angular
app.component.html
app.component.ts
app.module.ts
<dx-tree-list id="tree-list"
    <!-- 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 { }
Vue
App.vue
<template>
    <div id="app-container">
        <DxTreeList
            id="tree-list"
        />
    </div>
</template>

<script setup lang="ts">
import 'devextreme/dist/css/dx.fluent.blue.light.css';
import { DxTreeList } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import React from 'react';
import 'devextreme/dist/css/dx.fluent.blue.light.css';
import { TreeList } from 'devextreme-react/tree-list';

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

export default App;

Bind the TreeList to Data

TreeList supports plain and hierarchical data structures. To use a hierarchical data source, set dataStructure to "tree" and define itemsExpr to specify the data field that contains child nodes. TreeList automatically generates identifiers for all nodes and builds the node hierarchy. Refer to the following demo for more information:

TreeList - Simple Array: Hierarchical Structure Demo

In a plain data structure, nodes must include unique identifiers and references to parent nodes. To specify data fields that store identifiers and references, use the keyExpr and parentIdExpr properties. Top-level nodes descend from the root node. To specify the root identifier, define rootValue.

TreeList can load and update data from different data source types. To use a local array, assign the array to dataSource. The following code snippet initializes a TreeList and creates columns for all data fields. All columns are equal width, and the column order follows the data source structure:

jQuery
index.js
data.js
$("#tree-list").dxTreeList({
    dataSource: employees,
    rootValue: -1,
    keyExpr: "ID",
    parentIdExpr: "HeadID"
});
const employees = [
    // ...
];
ASP.NET Core Controls
Index.cshtml
EmployeeDataController.cs
Employee.cs
EmployeeData.cs
@using ASP_NET_Core.Models

@(Html.DevExtreme().TreeList<Employee>()
    .ID("tree-list")
    .DataSource(d => d
        .Mvc().Controller("EmployeeData")
        .LoadAction("Get")
        .InsertAction("Insert")
        .UpdateAction("Update")
        .DeleteAction("Delete")
        .Key("ID")
    )
    .RootValue(-1)
    .ParentIdExpr("HeadID")
)
using ASP_NET_Core.Models;
using DevExtreme.AspNet.Data;
using DevExtreme.AspNet.Mvc;
using Microsoft.AspNetCore.Mvc;

namespace ASP_NET_Core.Controllers;

public class EmployeeDataController : Controller {

    [HttpGet]
    public object Get(DataSourceLoadOptions loadOptions) {
        return DataSourceLoader.Load(EmployeeData.Employees.OrderBy(e => e.OrderIndex), loadOptions);
    }

    // ...

}
namespace ASP_NET_Core.Models;
public class Employee {
    public int ID { get; set; }
    public int HeadID { get; set; }
    public string FullName { get; set; }
    public string Position { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Email { get; set; }
    public string Skype { get; set; }
    public string MobilePhone { get; set; }
    public string BirthDate { get; set; }
    public string HireDate { get; set; }
    public int OrderIndex { get; set; }
}
namespace ASP_NET_Core.Models;
static class EmployeeData {
    public static List<Employee> Employees = [
        // ...
    ];
}
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 { EmployeesService, type Employee } 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;
}

@Injectable({
    providedIn: 'root'
})
export class EmployeesService {
    private employees: Employee[] = [
        // ...
    ];

    getEmployees(): Employee[] {
        return this.employees;
    }
}
Vue
App.vue
employeesService.ts
<template>
    <div id="app-container">
        <DxTreeList
            :data-source="employees"
            :root-value="-1"
            key-expr="ID"
            parent-id-expr="HeadID">
        </DxTreeList>
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { DxTreeList } from 'devextreme-vue/tree-list'; 
import { employeesService, type Employee } from '../employeesService';

const employees = ref<Employee[]>(employeesService.getEmployees());
</script>
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[] = [
    // ...
];

let employeeData = [...employees];

export const employeesService = {
    getEmployees: (): Employee[] => employeeData,

    // ...
}
React
App.tsx
employeesService.ts
import React, { useState } from 'react';
import { TreeList } from 'devextreme-react/tree-list';
import { employeesService, type Employee } from './employeesService';

function App() {
    const [currentEmployees, setCurrentEmployees] = useState<Employee[]>(employeesService.getEmployees());

    return (
        <div className="App">
            <TreeList
                dataSource={currentEmployees}
                rootValue={-1}
                keyExpr="ID"
                parentIdExpr="HeadID">
            </TreeList>
        </div>
    );
}
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[] = [
    // ...
];

let employeeData = [...employees];

export const employeesService = {
    getEmployees: (): Employee[] => employeeData,

    // ...
}

To use another data source type, refer to the following help topics:

Expand Rows

Read Tutorial: TreeList - Expand and Collapse Rows

You can define expandedRowKeys to expand specific rows. Enable autoExpandAll to initialize all rows in the expanded state:

jQuery
index.js
$("#tree-list").dxTreeList({
    autoExpandAll: true,
    // ...
});
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .AutoExpandAll(true)
    @* ... *@
)
Angular
app.component.html
<dx-tree-list
    [autoExpandAll]="true"
>
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <DxTreeList
        :auto-expand-all="true"
    >
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { DxTreeList } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import { TreeList } from 'devextreme-react/tree-list';

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

Customize Columns

Read Tutorial: Columns - Overview

jQuery

Use the columns array to define and customize TreeList columns. Configure columns as objects to specify column options. To add columns with default options, specify dataField values as strings in the columns[] array.

Angular

Specify the columns array to define and customize TreeList columns. Specify each column's dataField value.

Vue

Specify the columns array to define and customize TreeList columns. To add a column, specify the dataField value.

React

Specify the columns array to define and customize TreeList columns. To add a column, specify the dataField value.

Reorder Columns

Read Tutorial: Column Reordering

To set the initial column order, arrange columns in the columns[] array as needed. Enable allowColumnReordering to allow users to reorder columns in the component UI.

The following code snippet also specifies dataType for BirthDate and HireDate columns to display string values as dates:

jQuery
index.js
$(function() {
    $("#tree-list").dxTreeList({
        columns: [ "FullName", "Position", {
            dataField: "BirthDate", 
            dataType: "date",
        }, {
            dataField: "HireDate", 
            dataType: "date",
        }, "City", "State", "Email", "MobilePhone", "Skype"],
        allowColumnReordering: true,
        // ...
    });
});
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .Columns(columns => {
        columns.AddFor(m => m.FullName);
        columns.AddFor(m => m.Position);
        columns.AddFor(m => m.BirthDate)
            .DataType(GridColumnDataType.Date);
        columns.AddFor(m => m.HireDate)
            .DataType(GridColumnDataType.Date);
        columns.AddFor(m => m.City);
        columns.AddFor(m => m.State);
        columns.AddFor(m => m.Email);
        columns.AddFor(m => m.MobilePhone);
        columns.AddFor(m => m.Skype);
    })
    @* ... *@
)
Angular
app.component.html
<dx-tree-list [allowColumnReordering]="true">
    <dxi-tree-list-column dataField="FullName"></dxi-tree-list-column>
    <dxi-tree-list-column dataField="Position"></dxi-tree-list-column>
    <dxi-tree-list-column
        dataField="BirthDate"
        dataType="date">
    </dxi-tree-list-column>
    <dxi-tree-list-column
        dataField="HireDate"
        dataType="date">
    </dxi-tree-list-column>
    <dxi-tree-list-column dataField="City"></dxi-tree-list-column>
    <dxi-tree-list-column dataField="State"></dxi-tree-list-column>
    <dxi-tree-list-column dataField="Email"></dxi-tree-list-column>
    <dxi-tree-list-column dataField="MobilePhone"></dxi-tree-list-column>
    <dxi-tree-list-column dataField="Skype"></dxi-tree-list-column>
</dx-tree-list>
Vue
App.vue
<template>
    <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>
</template>

<script setup lang="ts">
import { DxTreeList, DxColumn } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import { TreeList, Column } from 'devextreme-react/tree-list';

function App() {
    return (
        <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>
    );
}

Resize Columns

Read Tutorial: Column Sizing

In the default configuration, TreeList columns have equal widths (width is set to "auto"). To change the column layout, set each column's width property or enable columnAutoWidth to adjust all columns to fit cell values. To allow users to change the column layout, enable allowColumnResizing.

jQuery
index.js
$("#tree-list").dxTreeList({
    allowColumnResizing: true,
    columnAutoWidth: true,
    columns: [{
        dataField: "BirthDate", 
        dataType: "date",
        width: 100,
    }, {
        dataField: "HireDate", 
        dataType: "date",
        width: 100,
    }, /* ... */ ],
    // ...
});
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .AllowColumnResizing(true)
    .ColumnAutoWidth(true)
    .Columns(columns => {
        columns.AddFor(m => m.BirthDate)
            .DataType(GridColumnDataType.Date)
            .Width(100);
        columns.AddFor(m => m.HireDate)
            .DataType(GridColumnDataType.Date)
            .Width(100);
    })
    @* ... *@
)
Angular
app.component.html
<dx-tree-list
    [allowColumnResizing]="true"
    [columnAutoWidth]="true"
>
    <dxi-tree-list-column
        dataField="BirthDate"
        dataType="date"
        [width]="100"
    ></dxi-tree-list-column>
    <dxi-tree-list-column
        dataField="HireDate"
        dataType="date"
        [width]="100"
    ></dxi-tree-list-column>
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <DxTreeList
        :allow-column-resizing="true"
        :column-auto-width="true"
    >
        <DxColumn
            data-field="BirthDate"
            data-type="date"
            :width="100"
        />
        <DxColumn
            data-field="HireDate"
            data-type="date"
            :width="100"
        />
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { DxTreeList, DxColumn } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import { TreeList, Column } from 'devextreme-react/tree-list';

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

Fix Columns

Read Tutorial: Column Fixing

When the total column width exceeds the UI component width, a horizontal scroll bar appears. To keep specific columns visible, enable columnFixing. Set columns[].fixed to true to fix a column. Set fixedPosition to specify the fixed column position or create a sticky column. Users can also change column fixing options in the TreeList context menu.

jQuery
index.js
$("#tree-list").dxTreeList({
    columnFixing: { enabled: true },
    columns: [{
        dataField: "FullName", 
        fixed: true
    }, /* ... */ ],
    // ...
});
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .ColumnFixing(c => c.Enabled(true))
    .Columns(columns => {
        columns.AddFor(m => m.FullName)
            .Fixed(true);
    })
    @* ... *@
)
Angular
app.component.html
<dx-tree-list>
    <dxo-tree-list-column-fixing [enabled]="true"></dxo-tree-list-column-fixing>
    <dxi-tree-list-column
        dataField="FullName"
        [fixed]="true"
    ></dxi-tree-list-column>
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <DxTreeList>
        <DxColumnFixing :enabled="true" />
        <DxColumn
            data-field="FullName"
            :fixed="true"
        />
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { DxTreeList, DxColumnFixing, DxColumn } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import { TreeList, ColumnFixing, Column } from 'devextreme-react/tree-list';

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

Hide Columns

Read Tutorial: Hide a Column Using the API Read Tutorial: Column Chooser

To hide a TreeList column, set columns[].visible to false. If the columnChooser is enabled, users can restore hidden columns. To hide a column in both the component and the column chooser, omit the corresponding columns[] item.

jQuery
index.js
$("#tree-list").dxTreeList({
    columnChooser: { enabled: true },
    columns: [{
        dataField: "Email",
        visible: false
    }, /* ... */ ],
    // ...
});
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .ColumnChooser(c => c.Enabled(true))
    .Columns(columns => {
        columns.AddFor(m => m.Email)
            .Visible(false);
    })
    @* ... *@
)
Angular
app.component.html
<dx-tree-list>
    <dxo-tree-list-column-chooser [enabled]="true"></dxo-tree-list-column-chooser>
    <dxi-tree-list-column
        dataField="Email"
        [visible]="false"
    ></dxi-tree-list-column>
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <DxTreeList ... >
        <DxColumnChooser :enabled="true" />
        <DxColumn
            data-field="Email"
            :visible="false"
        />
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { DxTreeList, DxColumnChooser, DxColumn } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import { TreeList, ColumnChooser, Column } from 'devextreme-react/tree-list';

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

Sort Data

Read Tutorial: TreeList - Sorting

The sorting.mode property specifies whether users can sort TreeList records against single or multiple columns.

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

To sort data and change sort orders in the UI, click column headers. Hold Shift and click to sort data against multiple columns.

jQuery
index.js
$("#tree-list").dxTreeList({
    sorting: { mode: "multiple" },
    // ...
});
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .Sorting(s => s.Mode(GridSortingMode.Multiple))
    @* ... *@
)
Angular
app.component.html
<dx-tree-list>
    <dxo-tree-list-sorting mode="multiple"></dxo-tree-list-sorting>
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <DxTreeList ... >
        <DxSorting mode="multiple" />
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { DxTreeList, DxSorting, DxColumn } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import { TreeList, Sorting, Column } from 'devextreme-react/tree-list';

function App() {
    return (
        <div className="App">
            <TreeList ... >
                <Sorting mode="multiple" />
                {/* ... */}
            </TreeList>
        </div>
    );
}
NOTE
Sorting overrides row order changes applied using drag and drop. Do not specify initial sort orders (columns[].sortOrder) to ensure row drag and drop works correctly.

Filter and Search Data

Read Tutorial: TreeList - Filtering and Searching

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

This tutorial uses the filterRow and searchPanel:

jQuery
index.js
$("#tree-list").dxTreeList({
    filterRow: { visible: true },
    searchPanel: { visible: true },
    // ...
});
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .FilterRow(f => f.Visible(true))
    .SearchPanel(s => s.Visible(true))
    @* ... *@
)
Angular
app.component.html
<dx-tree-list>
    <dxo-tree-list-filter-row [visible]="true"></dxo-tree-list-filter-row>
    <dxo-tree-list-search-panel [visible]="true"></dxo-tree-list-search-panel>
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <DxTreeList ... >
        <DxFilterRow :visible="true" />
        <DxSearchPanel :visible="true" />
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { DxTreeList, DxFilterRow, DxSearchPanel } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import { TreeList, FilterRow, SearchPanel } from 'devextreme-react/tree-list';

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

Edit and Validate Data

Read Tutorial: TreeList - Editing

Users can add new records and update or delete existing records. To allow these operations, enable the following editing options:

TreeList supports multiple edit modes. This tutorial uses the pop-up edit mode.

This tutorial also implements data validation to check that edited data is valid before saving. Required rules are configured to ensure certain column values are never empty:

jQuery
index.js
$("#tree-list").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" }]
    }, /* ... */ ],
    // ...
});
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .Editing(e => e
        .Mode(GridEditMode.Popup)
        .AllowAdding(true)
        .AllowDeleting(true)
        .AllowUpdating(true)
    )
    .Columns(columns => {
        columns.AddFor(m => m.FullName)
            .ValidationRules(v => v.AddRequired());
        columns.AddFor(m => m.Position)
            .ValidationRules(v => v.AddRequired());
        columns.AddFor(m => m.BirthDate)
            .ValidationRules(v => v.AddRequired());
        columns.AddFor(m => m.HireDate)
            .ValidationRules(v => v.AddRequired());
        columns.AddFor(m => m.State)
            .ValidationRules(v => v.AddRequired());
    })
    @* ... *@
)
Angular
app.component.html
<dx-tree-list>
    <dxo-tree-list-editing
        mode="popup"
        [allowUpdating]="true"
        [allowDeleting]="true"
        [allowAdding]="true"
    ></dxo-tree-list-editing>
    <dxi-tree-list-column dataField="FullName">
        <dxi-tree-list-validation-rule type="required"></dxi-tree-list-validation-rule>
    </dxi-tree-list-column>
    <dxi-tree-list-column dataField="Position">
        <dxi-tree-list-validation-rule type="required"></dxi-tree-list-validation-rule>
    </dxi-tree-list-column>
    <dxi-tree-list-column dataField="BirthDate">
        <dxi-tree-list-validation-rule type="required"></dxi-tree-list-validation-rule>
    </dxi-tree-list-column>
    <dxi-tree-list-column dataField="HireDate">
        <dxi-tree-list-validation-rule type="required"></dxi-tree-list-validation-rule>
    </dxi-tree-list-column>
    <dxi-tree-list-column dataField="State">
        <dxi-tree-list-validation-rule type="required"></dxi-tree-list-validation-rule>
    </dxi-tree-list-column>
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <DxTreeList>
        <DxEditing
            mode="popup"
            :allow-updating="true"
            :allow-adding="true"
            :allow-deleting="true"
        />
        <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>
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { DxTreeList, DxEditing, DxColumn, DxRequiredRule } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import { TreeList, Editing, Column, RequiredRule } from 'devextreme-react/tree-list';

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

The TreeList component does not support editing hierarchical data sources out of the box. To configure editing operations for hierarchical data sources, refer to the following example:

View on GitHub

Select Records

Read Tutorial: TreeList - Selection

TreeList supports single- and multiple-row selection. To enable row selection, configure the selection.mode property.

Handle onSelectionChanged to obtain the selected records at runtime. This tutorial uses onSelectionChanged to display the selected employee names in an element outside the component:

jQuery
index.js
index.html
$("#tree-list").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="tree-list"></div>
            <p id="selected-employee"></p>
        </div>
    </body>
</html>
ASP.NET Core Controls
Index.cshtml
<div id="app-container">
    @(Html.DevExtreme().TreeList<Employee>()
        .Selection(s => s.Mode(SelectionMode.Single))
        .OnSelectionChanged("handleSelectionChanged")
        @* ... *@
    )
    <p id="selected-employee"></p>
</div>

<script>
    function handleSelectionChanged(e) {
        e.component.byKey(e.currentSelectedRowKeys[0]).done((employee) => {
            if (employee) {
                $('#selected-employee').text(`Selected employee: ${employee.FullName}`);
            }
        });
    }
</script>
Angular
app.component.html
app.component.ts
<div id="app-container">
    <dx-tree-list (onSelectionChanged)="selectEmployee($event)">
        <dxo-tree-list-selection mode="single"></dxo-tree-list-selection>
        <!-- ... -->
    </dx-tree-list>
    @if (selectedEmployee) {
        <p id="selected-employee">
            Selected employee: {{ selectedEmployee.FullName }}
        </p>
    }
</div>
import { Component } from '@angular/core';
import { DxTreeListTypes } from 'devextreme-angular/ui/tree-list';
import { Employee, EmployeesService } from './employees.service';

// ...
export class AppComponent {
    selectedEmployee: Employee;

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

    selectEmployee(e: DxTreeListTypes.SelectionChangedEvent) {
        e.component.byKey(e.currentSelectedRowKeys[0]).done(employee => {
            if(employee) {
                this.selectedEmployee = employee;
            }
        });
    }
}
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 setup lang="ts">
import { ref } from 'vue';
import { DxTreeList, DxSelection, type TreeListTypes } from 'devextreme-vue/tree-list';
import { type Employee } from '../employees.service';

const selectedEmployee = ref<Employee | undefined>();

function selectEmployee(e: TreeListTypes.SelectionChangedEvent): void {
    e.component.byKey(e.currentSelectedRowKeys[0]).done((employee: Employee) => {
        if (employee) {
            selectedEmployee.value = employee;
        }
    });
}
</script>
React
App.tsx
import React, { useCallback, useState } from 'react';
import { TreeList, Selection, type TreeListTypes } from 'devextreme-react/tree-list';
import { type Employee } from './employees';

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: TreeListTypes.SelectionChangedEvent): void => {
        e.component.byKey(e.currentSelectedRowKeys[0]).then((employee: Employee) => {
            setSelectedEmployee(employee);
        }).catch(() => {});
    }, []);

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

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.

IMPORTANT
If you add custom items to the TreeList toolbar, you must also specify the built-in items you need.
jQuery
index.js
const treeList = $("#tree-list").dxTreeList({
    autoExpandAll: true,
    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");
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .ID("tree-list")
    .AutoExpandAll(true)
    .Toolbar(t => t.Items(items => {
        items.Add()
            .Location(ToolbarItemLocation.After)
            .Widget(w => w.Button()
                .Text("Collapse All")
                .Width(136)
                .OnClick("handleCollapseAllButtonClick")
        );
        items.Add().Name(TreeListToolbarItem.AddRowButton).ShowText(ToolbarItemShowTextMode.Always);
        items.Add().Name(TreeListToolbarItem.ColumnChooserButton);
        items.Add().Name(TreeListToolbarItem.SearchPanel);
    }))
    @* ... *@
)

<script>
    function handleCollapseAllButtonClick(e) {
        const expanding = e.component.option('text') === 'Expand All';
        $('#tree-list').dxTreeList('instance').option({
            autoExpandAll: expanding,
            expandedRowKeys: [],
        });
        e.component.option('text', expanding ? 'Collapse All' : 'Expand All');
    }
</script>
Angular
app.component.html
app.component.ts
app.module.ts
<dx-tree-list
    [autoExpandAll]="expanded"
    [(expandedRowKeys)]="expandedRowKeys"
>
    <dxo-tree-list-toolbar>
        <dxi-tree-list-item location="after">
            <dx-button
                [text]="expanded ? 'Collapse All' : 'Expand All'"
                [width]="136"
                (onClick)="toggleExpansion()"
            >
            </dx-button>
        </dxi-tree-list-item>
        <dxi-tree-list-item
            name="addRowButton"
            showText="always"
        ></dxi-tree-list-item>
        <dxi-tree-list-item name="exportButton"></dxi-tree-list-item>
        <dxi-tree-list-item name="columnChooserButton"></dxi-tree-list-item>
        <dxi-tree-list-item name="searchPanel"></dxi-tree-list-item>
    </dxo-tree-list-toolbar>
    <!-- ... -->
</dx-tree-list>
// ...
export class AppComponent {
    expanded: boolean = true;
    expandedRowKeys: number[] = [];

    toggleExpansion(): void {
        this.expanded = !this.expanded;
        this.expandedRowKeys = [];
    }
}
import {
    // ...
    DxButtonModule
} from 'devextreme-angular';

@NgModule({
    // ...
    imports: [
        // ...
        DxButtonModule
    ],
})
export class AppModule { }
Vue
App.vue
<template>
    <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="toggleExpansion"
            />
        </template>
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { DxTreeList, DxToolbar, DxItem } from 'devextreme-vue/tree-list';
import { DxButton } from 'devextreme-vue/button';

const expanded = ref<boolean>(true);
const expandedRowKeys = ref<number[]>([]);

const toggleExpansion = (): void => {
    expanded.value = !expanded.value;
    expandedRowKeys.value = [];
};

</script>
React
App.tsx
import React, { useCallback, useState } from 'react';
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<number[]>([]);

    const toggleExpansion = useCallback(() => {
        setExpanded((prevExpanded: boolean) => !prevExpanded);
        setExpandedRowKeys([]);
    }, []);

    return (
        <TreeList
            autoExpandAll={expanded}
            expandedRowKeys={expandedRowKeys}
        >
            <Toolbar>
                <Item location="after">
                    <Button
                        text={expanded ? 'Collapse All' : 'Expand All'}
                        width={136}
                        onClick={toggleExpansion}
                    />
                </Item>
                <Item name="addRowButton" showText="always" />
                <Item name="exportButton" />
                <Item name="columnChooserButton" />
                <Item name="searchPanel" />
            </Toolbar>
            {/* ... */}
        </TreeList>
    );
}

Enable Row Drag & Drop

TreeList allows users to drag and drop rows to reorder records or modify the node hierarchy. To configure this feature, specify the following rowDragging options:

NOTE
Sorting overrides row order changes applied using drag and drop. Do not specify initial sort orders (columns[].sortOrder) to ensure row drag and drop functions correctly.
jQuery
index.js
const treeList = $("#tree-list").dxTreeList({
    rowDragging: {
        allowDropInsideItem: true,
        allowReordering: true,
        onDragChange(e) {
            const visibleRows = treeList.getVisibleRows();
            const sourceNode = treeList.getNodeByKey(e.itemData.ID);
            let 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) {
            const visibleRows = e.component.getVisibleRows();
            const sourceData = e.itemData;
            const targetData = visibleRows[e.toIndex].data;

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

                if (sourceData.HeadID !== targetData.HeadID) {
                    sourceData.HeadID = targetData.HeadID;
                    if (e.toIndex > e.fromIndex) {
                        targetIndex += 1;
                    }
                }
                employees.splice(sourceIndex, 1);
                employees.splice(targetIndex, 0, sourceData);
            }
            e.component.refresh();
        },
    },
    // ...
}).dxTreeList("instance");
ASP.NET Core Controls
Index.cshtml
EmployeeDataController.cs
@(Html.DevExtreme().TreeList<Employee>()
    .RowDragging(r => r
        .AllowDropInsideItem(true)
        .AllowReordering(true)
        .OnDragChange("handleDragChange")
        .OnReorder("handleReorder")
    )
    @* ... *@
)

<script>
    function handleDragChange(e) {
        const targetRow = e.component.getVisibleRows()[e.toIndex];
        if (!targetRow) {
            return;
        }

        const sourceNode = e.component.getNodeByKey(e.itemData.ID);
        let targetNode = targetRow.node;

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

    function handleReorder(e) {
        const treeList = e.component;
        const store = treeList.getDataSource().store();
        const visibleRows = treeList.getVisibleRows();
        const sourceData = e.itemData;
        const targetData = visibleRows[e.toIndex].data;
        const newOrderIndex = targetData.OrderIndex;
        const d = $.Deferred();

        if (e.dropInsideItem) {
            store.update(sourceData.ID, { HeadID: targetData.ID }).then(function() {
                treeList.refresh().then(d.resolve, d.reject);
            }, d.reject);
        } else {
            store.update(sourceData.ID, {
                OrderIndex: newOrderIndex,
                HeadID: targetData.HeadID,
            }).then(function() {
                treeList.refresh().then(d.resolve, d.reject);
            }, d.reject);
        }

        e.promise = d.promise();
    }
</script>
using System.Linq;
using System.Text.Json;
using ASP_NET_Core.Models;
using DevExtreme.AspNet.Data;
using DevExtreme.AspNet.Mvc;
using Microsoft.AspNetCore.Mvc;

namespace ASP_NET_Core.Controllers;

public class EmployeeDataController : Controller {

    [HttpPut]
    public IActionResult Update(int key, string values) {
        var employee = EmployeeData.Employees.FirstOrDefault(e => e.ID == key);
        if(employee == null)
            return NotFound();

        var oldOrderIndex = employee.OrderIndex;

        PopulateEmployee(employee, values);

        var newOrderIndex = employee.OrderIndex;

        if (oldOrderIndex != newOrderIndex) {
            employee.OrderIndex = oldOrderIndex;
            var sortedEmployees = EmployeeData.Employees
                .OrderBy(e => e.OrderIndex)
                .ToList();

            if (oldOrderIndex < newOrderIndex) {
                for(var i = oldOrderIndex + 1; i <= newOrderIndex; i++) {
                    sortedEmployees[i].OrderIndex--;
                }
            } else {
                for(var i = newOrderIndex; i < oldOrderIndex; i++) {
                    sortedEmployees[i].OrderIndex++;
                }
            }
            employee.OrderIndex = newOrderIndex;
        }

        return Ok(employee);
    }

}
Angular
app.component.html
app.component.ts
employees.service.ts
<dx-tree-list>
    <dxo-tree-list-row-dragging
        [allowDropInsideItem]="true"
        [allowReordering]="true"
        [onDragChange]="onDragChange"
        [onReorder]="onReorder"
    ></dxo-tree-list-row-dragging>
    <!-- ... -->
</dx-tree-list>
import { type DxTreeListTypes } from 'devextreme-angular/ui/tree-list';
import { EmployeesService, type Employee } from './employees.service';

// ...
export class AppComponent {
    employees: Employee[] = [];

    onDragChange(e: DxTreeListTypes.RowDraggingChangeEvent): void {
        const visibleRows = e.component.getVisibleRows();
        const sourceNode = e.component.getNodeByKey(e.itemData.ID);
        let targetNode = visibleRows[e.toIndex].node;

        while (targetNode?.data) {
            if (targetNode.data.ID === sourceNode.data.ID) {
                e.cancel = true;
                break;
            }
            const parentNode = targetNode.parent;
            if (!parentNode) {
                break;
            }
            targetNode = parentNode;
        }
    }

    onReorder(e: DxTreeListTypes.RowDraggingReorderEvent): void {
        const visibleRows = e.component.getVisibleRows();
        const sourceData = e.itemData;
        const targetData = visibleRows[e.toIndex].data;

        if (e.dropInsideItem) {
            e.itemData.HeadID = targetData.ID;
            e.component.refresh().catch(() => {
                // Handle error silently
            });
        } else {
            let targetIndex = this.employees.indexOf(targetData);

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

            this.employeesService.reorderEmployees(sourceData, targetIndex);
            this.employees = this.employeesService.getEmployees();
        }
    }
}
// ...
export class EmployeesService {
    reorderEmployees(sourceData: Employee, targetIndex: number): void {
        const sourceIndex = this.employees.indexOf(sourceData);
        this.employees.splice(sourceIndex, 1);
        this.employees.splice(targetIndex, 0, sourceData);
    }
}
Vue
App.vue
employeeService.ts
<template>
    <DxTreeList>
        <DxRowDragging
            :allow-drop-inside-item="true"
            :allow-reordering="true"
            :on-drag-change="onDragChange"
            :on-reorder="onReorder"
        />
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { DxTreeList, DxRowDragging, type DxTreeListTypes } from 'devextreme-vue/tree-list';

const onDragChange = (e: DxTreeListTypes.RowDraggingChangeEvent): void => {
    const visibleRows = e.component.getVisibleRows();
    const sourceNode = e.component.getNodeByKey(e.itemData.ID);
    let targetNode = visibleRows[e.toIndex].node;

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

        if (!targetNode.parent) {
            break;
        }
        targetNode = targetNode.parent;
    }
};

const onReorder = (e: DxTreeListTypes.RowDraggingReorderEvent): void => {
    const visibleRows = e.component.getVisibleRows();
    const sourceData = e.itemData;
    const targetData = visibleRows[e.toIndex].data;

    if (e.dropInsideItem) {
        e.itemData.HeadID = targetData.ID;
        employeesService.updateEmployee(e.itemData);
        e.component.refresh().catch(() => {
            // Handle error silently
        });
    } else {
        let targetIndex = employees.value.indexOf(targetData);

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

        employeesService.reorderEmployees(sourceData, targetIndex);
        employees.value = employeesService.getEmployees();
    }

    e.component.refresh();
};
</script>
let employeeData = [...employees];

export const employeesService = {
    reorderEmployees: (sourceData: Employee, targetIndex: number): void => {
        const sourceIndex = employeeData.indexOf(sourceData);
        employeeData.splice(sourceIndex, 1);
        employeeData.splice(targetIndex, 0, sourceData);
    },

    // ...
};
React
App.tsx
employeesService.ts
import React, { useCallback, useState } from 'react';
import { TreeList, RowDragging, type TreeListTypes } from 'devextreme-react/tree-list';
import { employeesService, type Employee } from './employeesService';

function App() {
    const [currentEmployees, setCurrentEmployees] = useState<Employee[]>(employeesService.getEmployees());

    const onDragChange = useCallback((e: any) => {
        const visibleRows = e.component.getVisibleRows();
        const sourceNode = e.component.getNodeByKey(e.itemData.ID);
        let targetNode: TreeListTypes.Node<Employee, number> | undefined = visibleRows[e.toIndex].node;

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

    const onReorder = useCallback((e: any) => {
        const visibleRows = e.component.getVisibleRows();
        const sourceData = e.itemData;
        const targetData = visibleRows[e.toIndex].data;

        if (e.dropInsideItem) {
            const updatedSourceData = { ...sourceData, HeadID: targetData.ID };
            employeesService.updateEmployee(updatedSourceData);
        } else {
            let targetIndex = currentEmployees.indexOf(targetData);
            if (sourceData.HeadID !== targetData.HeadID) {
                sourceData.HeadID = targetData.HeadID;
                if (e.toIndex > e.fromIndex) {
                    targetIndex += 1;
                }
            }
            employeesService.reorderEmployees(sourceData, targetIndex);
        }

        setCurrentEmployees(employeesService.getEmployees());
        e.component.refresh();
    }, [currentEmployees]);

    return (
        <TreeList>
            <RowDragging
                onDragChange={onDragChange}
                onReorder={onReorder}
                allowDropInsideItem={true}
                allowReordering={true}
            />
            {/* ... */}
        </TreeList>
    );
}
let employeeData = [...employees];

export const employeesService = {
    reorderEmployees: (sourceData: Employee, targetIndex: number): void => {
        const sourceIndex = employeeData.indexOf(sourceData);
        employeeData.splice(sourceIndex, 1);
        employeeData.splice(targetIndex, 0, sourceData);
    },

    // ...
};

View Demo

Enable Pagination

Read Tutorial: TreeList - Paging

TreeList can load data in pages. To configure pagination, set paging.enabled to true and configure the pageSize property:

jQuery
index.js
$("#tree-list").dxTreeList({
    paging: {
        enabled: true,
        pageSize: 12,
    },
    // ...
});
ASP.NET Core Controls
Index.cshtml
@(Html.DevExtreme().TreeList<Employee>()
    .Paging(p => p
        .Enabled(true)
        .PageSize(12)
    )
    @* ... *@
)
Angular
app.component.html
<dx-tree-list>
    <dxo-tree-list-paging 
        [enabled]="true"
        [pageSize]="12"
    ></dxo-tree-list-paging>
    <!-- ... -->
</dx-tree-list>
Vue
App.vue
<template>
    <DxTreeList>
        <DxPaging
            :enabled="true"
            :page-size="12"
        />
        <!-- ... -->
    </DxTreeList>
</template>

<script setup lang="ts">
import { DxTreeList, DxPaging } from 'devextreme-vue/tree-list';

</script>
React
App.tsx
import { TreeList, Paging } from 'devextreme-react/tree-list';

function App() {
    return (
        <TreeList>
            <Paging
                enabled={true}
                defaultPageSize={12} 
            />
            {/* ... */}
        </TreeList>
    );
}

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