Your search did not match any results.
Data Grid

Custom Data Source

DevExtreme provides the CustomStore component to load and edit data from a custom data source. In this demo, the CustomStore fetches data from a custom remote server and displays it in the DataGrid.

The communication between the CustomStore and the server is organized as follows:

  • The CustomStore sends data processing settings to the server (see the load function in the code below).
  • The server applies these settings to data and returns the processed dataset (you should write the server-side code for this).

Each setting has information about a data operation (sorting, filtering, etc.) and is present only if this operation is declared as remote in the DataGrid's remoteOperations property. If your server does not support a certain operation, do not declare it as remote to perform it on the client.

For more information about the communication between the client and server in DevExtreme, refer to the following help topic: Server-Side Data Processing.

If your server does not support any data operations, set remoteOperations to false and implement the load function as described in the following help topic: Client-Side Data Processing.

Backend API
Copy to CodeSandBox
import React from 'react'; import 'devextreme/data/odata/store'; import DataGrid, { Column, Paging, Pager } from 'devextreme-react/data-grid'; import CustomStore from 'devextreme/data/custom_store'; import 'whatwg-fetch'; function isNotEmpty(value) { return value !== undefined && value !== null && value !== ''; } const store = new CustomStore({ key: 'OrderNumber', load(loadOptions) { let params = '?'; [ 'skip', 'take', 'requireTotalCount', 'requireGroupCount', 'sort', 'filter', 'totalSummary', 'group', 'groupSummary', ].forEach((i) => { if (i in loadOptions && isNotEmpty(loadOptions[i])) { params += `${i}=${JSON.stringify(loadOptions[i])}&`; } }); params = params.slice(0, -1); return fetch(`${params}`) .then((response) => response.json()) .then((data) => ({ data:, totalCount: data.totalCount, summary: data.summary, groupCount: data.groupCount, })) .catch(() => { throw new Error('Data Loading Error'); }); }, }); const allowedPageSizes = [8, 12, 20]; class App extends React.Component { render() { return ( <DataGrid dataSource={store} showBorders={true} remoteOperations={true} > <Column dataField="OrderNumber" dataType="number" /> <Column dataField="OrderDate" dataType="date" /> <Column dataField="StoreCity" dataType="string" /> <Column dataField="StoreState" dataType="string" /> <Column dataField="Employee" dataType="string" /> <Column dataField="SaleAmount" dataType="number" format="currency" /> <Paging defaultPageSize={12} /> <Pager showPageSizeSelector={true} allowedPageSizes={allowedPageSizes} /> </DataGrid> ); } } 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="" /> <script src=""></script> <script src=""></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.js"); </script> <link rel="stylesheet" type="text/css" href="styles.css" /> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
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.0/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@22.2.5/cjs', 'devextreme-react': 'npm:devextreme-react@22.2.5', 'jszip': 'npm:jszip@3.7.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.5.20/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.1.72/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.42/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);