DevExtreme v24.2 is now available.

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

Your search did not match any results.

React Data Grid - Remote Virtual Scrolling

With remote virtual scrolling, pages are loaded from the server when they enter the viewport. To enable this feature, set the scrolling.mode property to "virtual" and the remoteOperations property to true. The latter informs the DataGrid that the server handles paging.

Backend API
import React from 'react'; import DataGrid, { Scrolling, Paging, Column, HeaderFilter, Search, } from 'devextreme-react/data-grid'; import * as AspNetData from 'devextreme-aspnet-data-nojquery'; const dataSource = AspNetData.createStore({ key: 'Id', loadUrl: 'https://js.devexpress.com/Demos/WidgetsGalleryDataService/api/Sales', }); const App = () => ( <DataGrid height={440} dataSource={dataSource} showBorders={true} remoteOperations={true} wordWrapEnabled={true} > <Scrolling mode="virtual" rowRenderingMode="virtual" /> <Paging defaultPageSize={100} /> <HeaderFilter visible={true}> <Search enabled={true} /> </HeaderFilter> <Column dataField="Id" width={90} /> <Column dataField="StoreName" caption="Store" width={150} /> <Column dataField="ProductCategoryName" caption="Category" width={120} /> <Column dataField="ProductName" caption="Product" /> <Column dataField="DateKey" caption="Date" dataType="date" format="yyyy-MM-dd" width={110} /> <Column dataField="SalesAmount" caption="Amount" format="currency" width={100}> <HeaderFilter groupInterval={1000} /> </Column> </DataGrid> ); export default App;
import React from 'react'; import DataGrid, { Scrolling, Paging, Column, HeaderFilter, Search, } from 'devextreme-react/data-grid'; import * as AspNetData from 'devextreme-aspnet-data-nojquery'; const dataSource = AspNetData.createStore({ key: 'Id', loadUrl: 'https://js.devexpress.com/Demos/WidgetsGalleryDataService/api/Sales', }); const App = () => ( <DataGrid height={440} dataSource={dataSource} showBorders={true} remoteOperations={true} wordWrapEnabled={true} > <Scrolling mode="virtual" rowRenderingMode="virtual" /> <Paging defaultPageSize={100} /> <HeaderFilter visible={true}> <Search enabled={true} /> </HeaderFilter> <Column dataField="Id" width={90} /> <Column dataField="StoreName" caption="Store" width={150} /> <Column dataField="ProductCategoryName" caption="Category" width={120} /> <Column dataField="ProductName" caption="Product" /> <Column dataField="DateKey" caption="Date" dataType="date" format="yyyy-MM-dd" width={110} /> <Column dataField="SalesAmount" caption="Amount" format="currency" width={100} > <HeaderFilter groupInterval={1000} /> </Column> </DataGrid> ); export default App;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.tsx'; ReactDOM.render( <App />, document.getElementById('app'), );
window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, jsx: 'react', }, meta: { 'react': { 'esModule': true, }, 'typescript': { 'exports': 'ts', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, 'devextreme-aspnet-data-nojquery': { 'esModule': true, }, 'openai': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', 'bundles:': '../../../../bundles/', 'externals:': '../../../../bundles/externals/', }, defaultExtension: 'js', map: { 'ts': 'npm:plugin-typescript@8.0.0/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', '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/prop-types.js', 'devextreme-aspnet-data-nojquery': 'npm:devextreme-aspnet-data-nojquery@3.0.0/index.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@3.4.4/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign', 'devextreme': 'npm:devextreme@link:../../packages/devextreme/artifacts/npm/devextreme/cjs', 'devextreme-react': 'npm:devextreme-react@link:../../packages/devextreme-react/npm/cjs', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.54/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12', '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/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', 'devextreme-cldr-data': 'npm:devextreme-cldr-data@1.0.3', // 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.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js', }, packages: { 'devextreme': { defaultExtension: 'js', }, 'devextreme-react': { main: 'index.js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/localization/messages': { format: 'json', defaultExtension: 'json', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.12/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, react: true, }, }; System.config(window.config);
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.js'; ReactDOM.render(<App />, document.getElementById('app'));
<!DOCTYPE html> <html lang="en"> <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=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/24.2.3/css/dx.light.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <link rel="stylesheet" type="text/css" href="styles.css" /> <script type="text/javascript"> System.import("./index.tsx"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
#gridContainer { height: 440px; }

In this demo, the server contains 1,000,000 records and returns 100 records per page. The page size is set using the paging.pageSize property. The larger it is, the fewer requests are sent to the server. To reduce the time required for rendering the loaded rows, set the scrolling.rowRenderingMode to "virtual" — the DataGrid renders only rows that are in the viewport.

NOTE

The data source in this demo is configured to return only 1000 records per request.