How can I help you?
Working with data in React Chart component
3 Feb 202624 minutes to read
The React Chart component supports multiple data binding approaches to suit different application scenarios. Data can be bound from local JSON arrays, dynamically loaded on demand with lazy loading, or fetched from remote services using various adaptor patterns. This guide covers all available data binding methods, helping you choose the right approach for your use case based on data size, performance requirements, and backend architecture.
Choosing a data binding approach
| Method | Best For | Advantages | Considerations |
|---|---|---|---|
| Local data | Small to medium datasets | Simple setup, no network latency, instant rendering | All data must be in memory |
| Common datasource | Multiple series sharing data | Reduces redundancy, single update point | Limited to data common across series |
| Lazy loading | Large datasets with scrolling | Loads only visible data, better performance | Requires server-side pagination |
| Remote data (OData/WebAPI) | Backend-driven data | Scalable, centralized data management, real-time updates | Network latency, requires service setup |
| Offline mode | Data caching with client-side actions | Eliminates repeated requests, faster interactions | Initial load time, memory constraints |
Local data
Bind simple JSON data to the chart using the dataSource property in the series configuration. Map the JSON fields to the xName and yName properties to specify which data fields represent the x and y axis values.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Category, ChartComponent, ColumnSeries, Inject, Legend, LineSeries, SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
import { data } from './datasource';
function App() {
const primaryxAxis = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis}>
<Inject services={[ColumnSeries, Legend, LineSeries, Category]}/>
<SeriesCollectionDirective>
<SeriesDirective dataSource={data} xName='month' type='Column' yName='sales' name='Sales'/>
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { AxisModel,Category,ChartComponent, ColumnSeries, Inject, Legend, LineSeries,
SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
import { data } from './datasource';
function App() {
const primaryxAxis: AxisModel = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis}>
<Inject services={[ColumnSeries, Legend, LineSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={data} xName='month' type='Column' yName='sales' name='Sales' />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));export let data = [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: 28 },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: 32 },
{ month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
];export let data: Object[] = [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: 28 },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: 32 },
{ month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
];Lazy loading
Lazy loading enables on-demand data retrieval, loading only the data required for the currently visible range. The chart fires the scrollEnd event when the user scrolls near the edge of the visible data range. In this event handler, retrieve the minimum and maximum values from the scrolled axis, request the corresponding data from your server, and append it to the chart. This approach is ideal for large datasets that would be expensive to load entirely into memory.
How lazy loading works:
- User scrolls the chart to view a different data range
-
scrollEndevent fires with current axis range information - Fetch the corresponding data from your server
- Append new data to the existing dataset
- Chart automatically re-renders with the updated data
import * as React from "react";
import * as ReactDOM from "react-dom";
import { ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject, ScrollBar, Zoom, LineSeries, Tooltip, DateTime, Crosshair } from '@syncfusion/ej2-react-charts';
import { Internationalization } from '@syncfusion/ej2-base';
function App() {
let chart;
let intl = new Internationalization();
function GetDateTimeData(start, end) {
let series1 = [];
let date;
let value = 30;
let option = {
skeleton: 'full',
type: 'dateTime'
};
let dateParser = intl.getDateParser(option);
let dateFormatter = intl.getDateFormat(option);
for (let i = 0; start <= end; i++) {
date = Date.parse(dateParser(dateFormatter(start)));
if (Math.random() > .5) {
value += (Math.random() * 10 - 5);
}
else {
value -= (Math.random() * 10 - 5);
}
if (value < 0) {
value = getRandomInt(20, 40);
}
let point1 = { x: new Date(date), y: Math.round(value) };
new Date(start.setDate(start.getDate() + 1));
series1.push(point1);
}
return series1;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
return <ChartComponent id='charts' ref={chart => chart = chart} primaryXAxis={{
title: 'Day',
valueType: 'DateTime',
edgeLabelPlacement: 'Shift',
skeleton: 'yMMM',
skeletonType: 'Date',
scrollbarSettings: {
range: {
minimum: new Date(2009, 0, 1),
maximum: new Date(2014, 0, 1)
},
enable: true,
}
}} primaryYAxis={{
title: 'Server Load',
labelFormat: '{value}MB'
}} crosshair={{ enable: true, lineType: 'Vertical' }} chartArea={{ border: { width: 0 } }} tooltip={{ enable: true }} legendSettings={{ visible: true }}
// scrollStart={this.scrollStart.bind(this)}
// scrollEnd={this.scrollEnd.bind(this)}
title='Network Load' height='450' width='100%'>
<Inject services={[LineSeries, DateTime, Tooltip, ScrollBar, Zoom, Crosshair]}/>
<SeriesCollectionDirective>
<SeriesDirective dataSource={GetDateTimeData(new Date(2009, 0, 1), new Date(2009, 8, 1))} xName='x' yName='y' type='Line' animation={{ enable: false }}>
</SeriesDirective>
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject,
ChartTheme, ScrollBar, Zoom, IScrollEventArgs, LineSeries, Tooltip,
DateTime, ILoadedEventArgs, Chart, Crosshair, ColumnSeries } from '@syncfusion/ej2-react-charts';
import { Internationalization } from '@syncfusion/ej2-base';
function App() {
let chart: ChartComponent;
let intl: Internationalization = new Internationalization();
function GetDateTimeData(start: Date, end: Date): { x: Date, y: number }[] {
let series1: { x: Date, y: number }[] = [];
let date: number;
let value: number = 30;
let option: DateFormatOptions = {
skeleton: 'full',
type: 'dateTime'
};
let dateParser: Function = intl.getDateParser(option);
let dateFormatter: Function = intl.getDateFormat(option);
for (let i: number = 0; start <= end; i++) {
date = Date.parse(dateParser(dateFormatter(start)));
if (Math.random() > .5) {
value += (Math.random() * 10 - 5);
} else {
value -= (Math.random() * 10 - 5);
}
if (value < 0) {
value = getRandomInt(20, 40);
}
let point1: { x: Date, y: number } = { x: new Date(date), y: Math.round(value) };
new Date(start.setDate(start.getDate() + 1));
series1.push(point1);
}
return series1;
}
function getRandomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
return <ChartComponent id='charts'
ref={chart => chart = chart}
primaryXAxis={{
title: 'Day',
valueType: 'DateTime',
edgeLabelPlacement: 'Shift',
skeleton: 'yMMM',
skeletonType: 'Date',
scrollbarSettings: {
range: {
minimum: new Date(2009, 0, 1),
maximum: new Date(2014, 0, 1)
},
enable: true,
}
}}
primaryYAxis={{
title: 'Server Load',
labelFormat: '{value}MB'
}}
crosshair={{ enable: true, lineType: 'Vertical' }}
chartArea={{ border: { width: 0 } }}
tooltip={{ enable: true }}
legendSettings={{ visible: true }}
// scrollStart={this.scrollStart.bind(this)}
// scrollEnd={this.scrollEnd.bind(this)}
title='Network Load' height='450' width='100%' >
<Inject services={[LineSeries, DateTime, Tooltip, ScrollBar, Zoom, Crosshair]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={GetDateTimeData(new Date(2009, 0, 1), new Date(2009, 8, 1))} xName='x' yName='y'
type='Line' animation={{ enable: false }}>
</SeriesDirective>
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));Common datasource
Bind the same JSON data to all series by setting the dataSource property at the chart level instead of the series level. This approach is useful when multiple series share the same dataset and you want to update data from a single source. Each series independently maps its own xName and yName properties to the common data fields.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Category, ChartComponent, ColumnSeries, Inject, Legend, LineSeries, SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
import { chartData } from './datasource';
function App() {
const primaryxAxis = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis} dataSource={chartData}>
<Inject services={[ColumnSeries, Legend, LineSeries, Category]}/>
<SeriesCollectionDirective>
<SeriesDirective xName='month' type='Column' yName='sales'/>
<SeriesDirective xName='month' type='Column' yName='sales1'/>
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { AxisModel,Category,ChartComponent, ColumnSeries, Inject, Legend, LineSeries,
SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
import { chartData } from './datasource';
function App() {
const primaryxAxis: AxisModel = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis} dataSource={chartData}>
<Inject services={[ColumnSeries, Legend, LineSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective xName='month' type='Column' yName='sales' />
<SeriesDirective xName='month' type='Column' yName='sales1' />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));export let chartData = [
{ month: 'Jan', sales: 35, sales1: 28 }, { month: 'Feb', sales: 28, sales1: 35 },
{ month: 'Mar', sales: 34, sales1: 32 }, { month: 'Apr', sales: 32, sales1: 34 },
{ month: 'May', sales: 40, sales1: 32 }, { month: 'Jun', sales: 32, sales1: 40 },
{ month: 'Jul', sales: 35, sales1: 55 }, { month: 'Aug', sales: 55, sales1: 35 },
{ month: 'Sep', sales: 38, sales1: 30 }, { month: 'Oct', sales: 30, sales1: 38 },
{ month: 'Nov', sales: 25, sales1: 32 }, { month: 'Dec', sales: 32, sales1: 25 }
];export let chartData: Object[] = [
{ month: 'Jan', sales: 35, sales1: 28 }, { month: 'Feb', sales: 28, sales1: 35 },
{ month: 'Mar', sales: 34, sales1: 32 }, { month: 'Apr', sales: 32, sales1: 34 },
{ month: 'May', sales: 40, sales1: 32 }, { month: 'Jun', sales: 32, sales1: 40 },
{ month: 'Jul', sales: 35, sales1: 55 }, { month: 'Aug', sales: 55, sales1: 35 },
{ month: 'Sep', sales: 38, sales1: 30 }, { month: 'Oct', sales: 30, sales1: 38 },
{ month: 'Nov', sales: 25, sales1: 32 }, { month: 'Dec', sales: 32, sales1: 25 }
];Remote data
Bind remote data from a web service by using the DataManager class. The DataManager simplifies communication with REST APIs, OData services, and custom web endpoints. It requires minimal configuration—typically just the service URL and an appropriate adaptor—then handles all request/response processing. Assign the DataManager instance to the dataSource property in the series, and map the response fields to xName and yName. Use the optional query property to filter, sort, or paginate data on the server.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query } from '@syncfusion/ej2-data';
import { Category, ChartComponent, ColumnSeries, Inject, Legend, LineSeries, SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/orders'
});
const query = new Query().take(5).where('Estimate', 'lessThan', 3, false);
const primaryxAxis = { valueType: 'Category' };
const primaryyAxis = { title: 'Freight rate in U.S. dollars' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis} primaryYAxis={primaryyAxis} title="Container freight rate">
<Inject services={[ColumnSeries, Legend, Category, LineSeries]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} xName='CustomerID' type='Column' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager,Query} from '@syncfusion/ej2-data'
import { AxisModel,Category,ChartComponent, ColumnSeries, Inject, Legend, LineSeries, SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/orders'
});
const query = new Query().take(5).where('Estimate', 'lessThan', 3, false);
const primaryxAxis: AxisModel = { valueType: 'Category' };
const primaryyAxis: AxisModel = { title: 'Freight rate in U.S. dollars' };
return <ChartComponent id='charts'
primaryXAxis={primaryxAxis}
primaryYAxis={primaryyAxis}
title="Container freight rate">
<Inject services={[ColumnSeries, Legend, Category, LineSeries]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} xName='CustomerID' type='Column' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));Binding data using ODataAdaptor
OData is a standardized protocol for creating and consuming data via REST APIs. Use the ODataAdaptor with DataManager to retrieve data from OData services. The adaptor automatically constructs the correct query syntax and handles standard OData conventions.
Example use case: Querying a product sales service that implements OData v3.0 filtering, sorting, and pagination.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, ODataAdaptor } from '@syncfusion/ej2-data';
import { ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.odata.org/V3/Northwind/Northwind.svc/Orders/',
adaptor: new ODataAdaptor(),
crossDomain: true
});
const query = new Query();
const primaryxAxis = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis} title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} xName='CustomerID' type='Column' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, ODataAdaptor } from '@syncfusion/ej2-data'
import { AxisModel, ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.odata.org/V3/Northwind/Northwind.svc/Orders/',
adaptor: new ODataAdaptor(),
crossDomain: true
});
const query = new Query();
const primaryxAxis: AxisModel = { valueType: 'Category' };
return <ChartComponent id='charts'
primaryXAxis={primaryxAxis}
title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} type='Column' xName='CustomerID' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));Binding data using ODataV4Adaptor
ODataV4 is an improved and more standardized version of the OData protocol, with enhanced query capabilities and better JSON support. Use the ODataV4Adaptor to consume ODataV4 services. For more information on ODataV4 specifications, refer to the OData v4 documentation.
When to use ODataV4Adaptor: If your backend service implements OData v4.0, prefer this adaptor over the older ODataAdaptor for better compliance and features.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, ODataV4Adaptor } from '@syncfusion/ej2-data';
import { ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.odata.org/V4/Northwind/Northwind.svc/Orders',
adaptor: new ODataV4Adaptor()
});
const query = new Query();
const primaryxAxis = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis} title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} type='Column' xName='CustomerID' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, ODataV4Adaptor } from '@syncfusion/ej2-data'
import { AxisModel, ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.odata.org/V4/Northwind/Northwind.svc/Orders',
adaptor: new ODataV4Adaptor()
});
const query = new Query();
const primaryxAxis: AxisModel = { valueType: 'Category' };
return <ChartComponent id='charts'
primaryXAxis={primaryxAxis}
title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} type='Column' xName='CustomerID' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));Web API adaptor
Use the WebApiAdaptor to consume custom REST APIs that follow a standard response format. This adaptor is ideal for backends that do not implement OData but provide REST endpoints returning JSON data.
Expected response format:
The Web API must return a JSON object with two properties:
-
Items: Array of data objects for the chart -
Count: Total number of records (useful for pagination)
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, WebApiAdaptor } from '@syncfusion/ej2-data';
import { ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/orders',
adaptor: new WebApiAdaptor()
});
const query = new Query();
const primaryxAxis = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis} title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} type='Column' xName='CustomerID' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, WebApiAdaptor } from '@syncfusion/ej2-data'
import { AxisModel, ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/orders',
adaptor: new WebApiAdaptor()
});
const query = new Query();
const primaryxAxis: AxisModel = { valueType: 'Category' };
return <ChartComponent id='charts'
primaryXAxis={primaryxAxis}
title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} type='Column' xName='CustomerID' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));Example response:
The Web API should structure its response as shown below. The Items array contains the actual data records, and Count indicates the total available records (supporting server-side pagination).
{
Items: [{..}, {..}, {..}, ...],
Count: 830
}
Custom adaptor
Create a custom adaptor by extending one of the built-in adaptors (typically ODataAdaptor) to add custom logic for request/response handling. Override the processResponse method to transform or enrich the response data. Common use cases include adding serial numbers, reformatting dates, or adding computed fields before the chart renders the data.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, ODataAdaptor } from '@syncfusion/ej2-data';
import { ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
import { SerialNoAdaptor } from './serialNoAdaptor';
function App() {
const dataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/orders',
adaptor: new SerialNoAdaptor()
});
const query = new Query();
const primaryxAxis = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis} title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} type='Column' xName='CustomerID' yName='Sno' query={query} />
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, ODataAdaptor } from '@syncfusion/ej2-data'
import { AxisModel, ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
import { SerialNoAdaptor } from './serialNoAdaptor';
function App() {
const dataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/orders',
adaptor: new SerialNoAdaptor()
});
const query = new Query();
const primaryxAxis: AxisModel = { valueType: 'Category' };
return <ChartComponent id='charts'
primaryXAxis={primaryxAxis}
title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} type='Column' xName='CustomerID' yName='Sno' query={query} />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import { setValue } from '@syncfusion/ej2-base';
import { ODataAdaptor } from '@syncfusion/ej2-data';
export class SerialNoAdaptor extends ODataAdaptor {
processResponse() {
let i = 0;
// calling base class processResponse function
const original = super.processResponse.apply(this, arguments);
if (!(original instanceof Array)) {
// adding serial number
original.result.forEach((item) => setValue('Sno', ++i, item));
}
return original;
}
}import { setValue } from '@syncfusion/ej2-base';
import { ODataAdaptor } from '@syncfusion/ej2-data';
export class SerialNoAdaptor extends ODataAdaptor {
public processResponse() {
let i: number = 0;
// calling base class processResponse function
const original: any = super.processResponse.apply(this, arguments);
if (!(original instanceof Array)) {
// adding serial number
original.result.forEach((item: object) => setValue('Sno', ++i, item));
}
return original;
}
}Offline mode
When using remote data binding, all filtering, sorting, and pagination normally happen on the server. To improve responsiveness and reduce server load, enable offline mode: the chart loads all data once during initialization, then handles all interactions client-side. Set the offline property of DataManager to true to activate this behavior.
Use offline mode when:
- Your dataset is relatively small (fits comfortably in browser memory)
- You want instant filtering and sorting without server round-trips
- Network latency is a significant usability concern
- You prefer to minimize server requests during user interactions
Caution: Offline mode loads the entire dataset at once, which may impact initial load time and memory usage for large datasets.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, ODataAdaptor } from '@syncfusion/ej2-data';
import { ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/orders',
adaptor: new ODataAdaptor(),
offline: true
});
const query = new Query();
const primaryxAxis = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis} title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} type='Column' xName='CustomerID' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { DataManager, Query, ODataAdaptor } from '@syncfusion/ej2-data'
import { AxisModel, ChartComponent, Inject, SeriesCollectionDirective, SeriesDirective, Category, ColumnSeries } from '@syncfusion/ej2-react-charts';
function App() {
const dataManager = new DataManager({
url: 'https://services.syncfusion.com/react/production/api/orders',
adaptor: new ODataAdaptor(),
offline: true
});
const query = new Query();
const primaryxAxis: AxisModel = { valueType: 'Category' };
return <ChartComponent id='charts'
primaryXAxis={primaryxAxis}
title="Order Details">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={dataManager} type='Column' xName='CustomerID' yName='Freight' query={query} />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));Empty points
Data points with null or undefined values are treated as empty points. Empty data points are skipped and not rendered in the chart. When using the points property to define individual data items, customize empty points with the emptyPointSettings property in the series configuration. By default, empty points create a gap in the series line or bar.
Default behavior: Empty points use the Gap mode, which leaves a blank space in the chart visualization.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Category, ChartComponent, ColumnSeries, Inject, LineSeries, SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
import { data } from './datasource';
function App() {
const primaryxAxis = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis}>
<Inject services={[ColumnSeries, LineSeries, ColumnSeries, Category]}/>
<SeriesCollectionDirective>
<SeriesDirective dataSource={data} xName='month' type='Column' yName='sales' name='Sales'/>
<SeriesDirective dataSource={data} xName='month' type='Line' yName='sales' name='Sales'/>
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { AxisModel,Category,ChartComponent, ColumnSeries, Inject, LineSeries,
SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
import { data } from './datasource';
function App() {
const primaryxAxis: AxisModel = { valueType: 'Category' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis}>
<Inject services={[ColumnSeries, LineSeries, ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={data} xName='month' type='Column' yName='sales' name='Sales' />
<SeriesDirective dataSource={data} xName='month' type='Line' yName='sales' name='Sales' />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));export let data = [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: null },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: undefined },
{ month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
];export let data: Object[] = [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: null },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: undefined },
{ month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
];Empty point color
Assign a specific color to empty points by setting the fill property in the emptyPointSettings object. This allows you to visually distinguish empty data points from regular data in your chart.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Category, ChartComponent, ColumnSeries, Inject, LineSeries, SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
import { data } from './datasource';
function App() {
const primaryxAxis = { valueType: 'Category' };
const emptyPointSettings1 = { mode: 'Average', fill: 'pink' };
const emptyPointSettings2 = { mode: 'Zero', fill: 'green' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis}>
<Inject services={[ColumnSeries, LineSeries, ColumnSeries, Category]}/>
<SeriesCollectionDirective>
<SeriesDirective dataSource={data} xName='month' type='Column' yName='sales' name='Sales' emptyPointSettings={emptyPointSettings1}/>
<SeriesDirective dataSource={data} xName='month' type='Line' yName='sales' name='Sales' emptyPointSettings={emptyPointSettings2}/>
</SeriesCollectionDirective>
</ChartComponent>;
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { AxisModel,Category,ChartComponent, ColumnSeries, EmptyPointSettingsModel,
Inject, LineSeries, SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-react-charts';
import { data } from './datasource';
function App() {
const primaryxAxis: AxisModel = { valueType: 'Category' };
const emptyPointSettings1: EmptyPointSettingsModel = { mode: 'Average', fill: 'pink' };
const emptyPointSettings2: EmptyPointSettingsModel = { mode: 'Zero', fill: 'green' };
return <ChartComponent id='charts' primaryXAxis={primaryxAxis}>
<Inject services={[ColumnSeries, LineSeries, ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective dataSource={data} xName='month' type='Column' yName='sales' name='Sales' emptyPointSettings={emptyPointSettings1} />
<SeriesDirective dataSource={data} xName='month' type='Line' yName='sales' name='Sales' emptyPointSettings={emptyPointSettings2} />
</SeriesCollectionDirective>
</ChartComponent>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));export let data = [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: null },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: undefined },
{ month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
];export let data: Object[] = [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: null },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: undefined },
{ month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
];Handling no data
When the chart has no data available to render, use the noDataTemplate property to display a custom layout within the chart area. This template can include messages, images, icons, or interactive elements (such as a load button) to guide the user. The template maintains design consistency and improves user experience when data is unavailable. Once data becomes available, the chart automatically updates and replaces the template with the visualization.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Category, ChartComponent, ColumnSeries, Inject, LineSeries, SeriesCollectionDirective, SeriesDirective, ILoadedEventArgs } from '@syncfusion/ej2-react-charts';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
function App() {
var chartInstance;
const [hasData, setHasData] = React.useState(false);
const SAMPLE_CSS = `
#noDataTemplateContainer {
height: inherit;
width: inherit;
}
.light-bg {
background-color: #fafafa;
color: #000000;
}
.template-align img {
max-width: 150px;
/* Adjust size as needed */
max-height: 150px;
margin-top: 55px;
}
.load-data-btn {
border-radius: 4px;
}
.template-align {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
flex-direction: column;
}
#syncfusionButtonContainer {
margin-top: 5px;
}`;
// Sample data that will be loaded when button is clicked
const sampleData = [
{ x: 'January', y: 19173 },
{ x: 'February', y: 17726 },
{ x: 'March', y: 19874 },
{ x: 'April', y: 19391 },
{ x: 'May', y: 20072 },
{ x: 'June', y: 19233 }
];
React.useEffect(() => {
if (hasData) {
const buttonContainer = document.getElementById("syncfusionButtonContainer");
if (buttonContainer) {
ReactDOM.unmountComponentAtNode(buttonContainer);
}
}
}, [hasData]);
const noDataTemplate = `
<div id="noDataTemplateContainer" class="light-bg">
<div class="template-align">
<img src="no-data.png" alt="No Data"/>
</div>
<div class="template-align">
<p style="font-size: 15px; margin: 10px 0 10px;"><strong>No data available to display.</strong></p>
</div>
<div class="template-align">
<div id="syncfusionButtonContainer"></div>
</div>
</div>
`;
// Function to load data when button is clicked
const loadData = () => {
if (chartInstance) {
chartInstance.series[0].dataSource = sampleData;
}
};
// Function to load data when button is clicked
const loadedChartData = () => {
if (!hasData) {
const buttonContainer = document.getElementById("syncfusionButtonContainer");
if (buttonContainer && !buttonContainer.hasChildNodes()) {
// Create the button element using React.createElement
const buttonElement = React.createElement(ButtonComponent, {
id: "loadDataButton",
content: "Load Data",
iconCss: "e-icons e-refresh",
cssClass: "load-data-btn e-outline",
isPrimary: false,
onClick: loadData
});
const root = createRoot(buttonContainer);
root.render(buttonElement);
}
}
};
return (
<div>
{/* Custom No Data Template with Button */}
<style>{SAMPLE_CSS}</style>
{/* Chart Component */}
<div id="chart-container">
<ChartComponent id='chart' ref={g => chart = g} primaryXAxis={{ valueType: 'Category', majorGridLines: { width: 0 }, majorTickLines: { width: 0 }, }} chartArea={{ border: { width: 0 } }}
primaryYAxis={{ title: 'Production (in million pounds)', titleStyle: { fontWeight: '600' }, majorTickLines: { width: 0 }, lineStyle: { width: 0 } }} loaded={loadedChartData} noDataTemplate={noDataTemplate} title="Milk Production in US - 2025" subTitle="Source: nass.usda.gov">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective xName='x' yName='y' type='Column' />
</SeriesCollectionDirective>
</ChartComponent>
</div>
</div>
);
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));import * as React from "react";
import * as ReactDOM from "react-dom";
import { createRoot } from 'react-dom/client';
import {
AxisModel, Category, ChartComponent, ColumnSeries, Inject, LineSeries,
SeriesCollectionDirective, SeriesDirective
} from '@syncfusion/ej2-react-charts';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
function App() {
var chartInstance: ChartComponent | null;
const [hasData, setHasData] = React.useState(false);
const SAMPLE_CSS = `
#noDataTemplateContainer {
height: inherit;
width: inherit;
}
.light-bg {
background-color: #fafafa;
color: #000000;
}
.template-align img {
max-width: 150px;
/* Adjust size as needed */
max-height: 150px;
margin-top: 55px;
}
.load-data-btn {
border-radius: 4px;
}
.template-align {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
flex-direction: column;
}
#syncfusionButtonContainer {
margin-top: 5px;
}`;
// Sample data that will be loaded when button is clicked
const sampleData = [
{ x: 'January', y: 19173 },
{ x: 'February', y: 17726 },
{ x: 'March', y: 19874 },
{ x: 'April', y: 19391 },
{ x: 'May', y: 20072 },
{ x: 'June', y: 19233 }
];
React.useEffect(() => {
if (hasData) {
const buttonContainer = document.getElementById("syncfusionButtonContainer");
if (buttonContainer) {
ReactDOM.unmountComponentAtNode(buttonContainer);
}
}
}, [hasData]);
const noDataTemplate = `
<div id="noDataTemplateContainer" class="light-bg">
<div class="template-align">
<img src="no-data.png" alt="No Data"/>
</div>
<div class="template-align">
<p style="font-size: 15px; margin: 10px 0 10px;"><strong>No data available to display.</strong></p>
</div>
<div class="template-align">
<div id="syncfusionButtonContainer"></div>
</div>
</div>
`;
// Function to load data when button is clicked
const loadData = () => {
if (chartInstance) {
(chartInstance as ChartComponent).series[0].dataSource = sampleData;
}
};
// Function to load data when button is clicked
const loadedChartData = () => {
if (!hasData) {
const buttonContainer = document.getElementById("syncfusionButtonContainer");
if (buttonContainer && !buttonContainer.hasChildNodes()) {
// Create the button element using React.createElement
const buttonElement = React.createElement(ButtonComponent, {
id: "loadDataButton",
content: "Load Data",
iconCss: "e-icons e-refresh",
cssClass: "load-data-btn e-outline",
isPrimary: false,
onClick: loadData
});
const root = createRoot(buttonContainer);
root.render(buttonElement);
}
}
};
return (
<div>
{/* Custom No Data Template with Button */}
<style>{SAMPLE_CSS}</style>
{/* Chart Component */}
<div id="chart-container">
<ChartComponent id='chart' ref={g => chart = g} primaryXAxis={{ valueType: 'Category', majorGridLines: { width: 0 }, majorTickLines: { width: 0 } }} chartArea={{ border: { width: 0 } }} primaryYAxis={{ title: 'Production (in million pounds)', titleStyle: { fontWeight: '600' },
majorTickLines: { width: 0 }, lineStyle: { width: 0 } }} loaded={loadedChartData} noDataTemplate={noDataTemplate} title="Milk Production in US - 2025" subTitle="Source: nass.usda.gov">
<Inject services={[ColumnSeries, Category]} />
<SeriesCollectionDirective>
<SeriesDirective xName='x' yName='y'type='Column' />
</SeriesCollectionDirective>
</ChartComponent>
</div>
</div>
);
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));export let data = [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: null },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: undefined },
{ month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
];export let data: Object[] = [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: null },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: undefined },
{ month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
];