DevExtreme v23.1 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.
Tree List

Load Data on Demand

The TreeList can load a remote dataset dynamically as a user expands nodes. The dataset must have a plain structure.

This feature requires client- and server-side configurations. To configure the client-side part, do the following:

  1. Send an expanded node's ID to the server
    For this, implement the CustomStore's load function. In this demo, we do it in the dataSource configuration object.

  2. Delegate filtering to the server
    Set the remoteOperations.filtering property to true.

  3. Specify the data field that defines whether the node has children
    Use the hasItemsExpr property to set this data field.

Server-side implementation is available in the ASP.NET Core and ASP.NET MVC versions of this demo under the TreeListDataController.cs tab.


This demo uses a simple data bind technique that is useful for data display purposes only. When a user clicks a node, TreeList receives a JSON object from the server, which is based on the parentIds property value. This technique does not support the built-in data process operations in TreeList on the server.

If your project needs to process data, do one of the following instead:

Backend API
Copy to CodeSandBox
import React from 'react'; import { TreeList, RemoteOperations, Column } from 'devextreme-react/tree-list'; import 'whatwg-fetch'; const dataSource = { load(loadOptions) { const parentIdsParam = loadOptions.parentIds; const url = new URL(''); if (parentIdsParam) { parentIdsParam.forEach((id) => { url.searchParams.append('parentIds', id); }); } return fetch(url) .then((response) => response.json()) .catch(() => { throw new Error('Data Loading Error'); }); }, }; class App extends React.Component { render() { return ( <TreeList id="treelist" dataSource={dataSource} showBorders={true} keyExpr="id" parentIdExpr="parentId" hasItemsExpr="hasItems" rootValue="" > <RemoteOperations filtering={true} /> <Column dataField="name" /> <Column width={100} customizeText={this.customizeText} dataField="size" /> <Column width={150} dataField="createdDate" dataType="date" /> <Column width={150} dataField="modifiedDate" dataType="date" /> </TreeList> ); } customizeText(e) { if (e.value !== null) { return `${Math.ceil(e.value / 1024)} KB`; } return null; } } export default App;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.js'; ReactDOM.render( <App />, document.getElementById('app'), );
<!DOCTYPE html> <html> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="" /> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src=""></script> <script src=""></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.js"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
#treelist { max-height: 440px; }
window.config = { transpiler: 'plugin-babel', meta: { 'devextreme/localization.js': { 'esModule': true, }, }, paths: { 'npm:': '', }, defaultExtension: 'js', map: { 'react': 'npm:react@17.0.2/umd/react.development.js', 'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js', 'prop-types': 'npm:prop-types@15.8.1/prop-types.js', 'whatwg-fetch': 'npm:whatwg-fetch@2.0.4/fetch.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@23.1.5/cjs', 'devextreme-react': 'npm:devextreme-react@23.1.5', 'jszip': 'npm:jszip@3.7.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.6.2/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.1/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.48/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.11', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', // SystemJS plugins 'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'devextreme': { defaultExtension: 'js', }, 'devextreme-react': { main: 'index.js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.11/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, react: true, }, }; System.config(window.config);