Whenever the visible range changes, the chart requests data from the server, which returns a reduced view of the dataset, in this case using a very simple take every nth point method. The overview is created manually because it does not share data with the main chart but has a reduced view of the entire dataset.
import { Subject, debounceTime } from "rxjs";
import {
easing,
EAutoRange,
EAxisAlignment,
EHorizontalAnchorPoint,
EVerticalAnchorPoint,
EWrapTo,
EXyDirection,
FastLineRenderableSeries,
MouseWheelZoomModifier,
NativeTextAnnotation,
NumberRange,
NumericAxis,
SciChartOverview,
SciChartSurface,
XAxisDragModifier,
XyDataSeries,
YAxisDragModifier,
ZoomExtentsModifier,
ZoomPanModifier,
ECoordinateMode,
AxisBase2D,
} from "scichart";
import { appTheme } from "./appTheme";
export const getChartsInitializationApi = () => {
let mainSurface: SciChartSurface;
let mainXAxis: AxisBase2D;
let mainDataSeries: XyDataSeries;
const createMainChart = async (rootElement: string | HTMLDivElement) => {
const { wasmContext, sciChartSurface } = await SciChartSurface.create(rootElement, {
theme: appTheme.SciChartJsTheme,
});
const xAxis = new NumericAxis(wasmContext, {
axisAlignment: EAxisAlignment.Bottom,
visibleRange: new NumberRange(4000000, 5000000),
autoRange: EAutoRange.Never,
labelPrecision: 0,
useNativeText: true,
});
sciChartSurface.xAxes.add(xAxis);
const yAxis = new NumericAxis(wasmContext, {
axisAlignment: EAxisAlignment.Right,
visibleRange: new NumberRange(-5000, 5000),
autoRange: EAutoRange.Never,
labelPrecision: 0,
useNativeText: true,
});
sciChartSurface.yAxes.add(yAxis);
const dataSeries = new XyDataSeries(wasmContext, { containsNaN: false, isSorted: true });
const rendSeries = new FastLineRenderableSeries(wasmContext, {
dataSeries,
strokeThickness: 2,
stroke: appTheme.VividOrange,
});
sciChartSurface.renderableSeries.add(rendSeries);
rendSeries.rolloverModifierProps.tooltipTextColor = "black";
rendSeries.rolloverModifierProps.showRollover = true;
sciChartSurface.chartModifiers.add(
new ZoomExtentsModifier({ xyDirection: EXyDirection.YDirection }),
new XAxisDragModifier(),
new YAxisDragModifier(),
new ZoomPanModifier({ enableZoom: true }),
new MouseWheelZoomModifier()
);
// Create an observable stream
const subject = new Subject<NumberRange>();
// Push visible range changes into the observable
xAxis.visibleRangeChanged.subscribe(async (args) => {
subject.next(args.visibleRange);
});
// subscribe to the observable with a debounce
subject.pipe(debounceTime(250)).subscribe((r: NumberRange) => {
// Fetch data and update the dataSeries
loadPoints(r.min, r.max, sciChartSurface.domCanvas2D.width, dataSeries)
.then(() => {
// Update the y axis
const yRange = yAxis.getWindowedYRange(null);
yAxis.animateVisibleRange(yRange, 250, easing.outExpo);
})
.catch((err) =>
showError(
sciChartSurface,
"Server data is unavailable. Please do npm run build, then npm start and access the site at localhost:3000"
)
);
});
mainSurface = sciChartSurface;
mainXAxis = xAxis;
mainDataSeries = dataSeries;
return { sciChartSurface };
};
const createOverview = async (rootElement: string | HTMLDivElement) => {
const overview = await SciChartOverview.create(mainSurface, rootElement, { theme: appTheme.SciChartJsTheme });
const overviewData = new XyDataSeries(mainSurface.webAssemblyContext2D, { containsNaN: false, isSorted: true });
// Load the full dataSet
loadPoints(0, 10000000, overview.overviewSciChartSurface.domCanvas2D.width, overviewData).catch((err) => {});
const overviewSeries = overview.overviewSciChartSurface.renderableSeries.get(0);
overviewSeries.dataSeries = overviewData;
overview.overviewSciChartSurface.zoomExtents();
return { sciChartSurface: overview.overviewSciChartSurface };
};
const afterOverviewInit = () => {
// Load initial data
loadPoints(
mainXAxis.visibleRange.min,
mainXAxis.visibleRange.max,
mainSurface.domCanvas2D.width,
mainDataSeries
)
.then(() => {
mainSurface.zoomExtents();
})
.catch((err) =>
showError(
mainSurface,
"Server data is unavailable. Please do npm run build, then npm start and access the site at localhost:3000"
)
);
};
return { createMainChart, createOverview, afterOverviewInit };
};
const loadPoints = async (xFrom: number, xTo: number, chartWidth: number, dataSeries: XyDataSeries) => {
chartWidth = Math.floor(chartWidth);
const response = await fetch(`api/data/${xFrom}-${xTo}/${chartWidth}`);
const data: { x: number[]; y: number[] } = await response.json();
console.log(`Loaded ${data.x.length} points`);
dataSeries.clear();
dataSeries.appendRange(data.x, data.y);
};
const showError = (sciChartSurface: SciChartSurface, message: string) => {
if (!sciChartSurface.annotations.getById("error")) {
sciChartSurface.annotations.add(
new NativeTextAnnotation({
id: "error",
text: message,
x1: 0.5,
y1: 0.5,
textColor: "red",
fontSize: 24,
horizontalAnchorPoint: EHorizontalAnchorPoint.Center,
verticalAnchorPoint: EVerticalAnchorPoint.Center,
xCoordinateMode: ECoordinateMode.Relative,
yCoordinateMode: ECoordinateMode.Relative,
lineSpacing: 5,
wrapTo: EWrapTo.ViewRect,
})
);
}
};
This example demonstrates a high performance, virtualized charting solution implemented with React and SciChart.js. The application dynamically loads a subset of data based on the current zoom and pan state of the main chart while concurrently displaying an overview chart that represents the entire dataset to provide context.
The implementation utilizes the <SciChartReact/> component within a functional React component using hooks. The main chart initializes with a numeric x-axis and y-axis, and uses a FastLineRenderableSeries to render data fetched asynchronously via the fetch API. To optimize performance and avoid unnecessary data requests, RxJS's debounceTime operator is used to throttle updates triggered by visibleRangeChanged events. The overview chart is created manually using SciChartOverview with its own data series, highlighting a master-detail chart synchronization pattern. As the chart is zoomed or scrolled, data is fetched from REST endpoints using the fetch API, and the number of loaded data points is adjusted, providing a virtualized big-data chart.
This solution delivers real-time updates through efficient asynchronous data loading and utilizes advanced interaction modifiers such as zoom, pan, and drag for an engaging user experience. The overview chart efficiently represents the full data range, while the main chart focuses on a detailed view with optimized data rendering. Developers can further explore robust React chart integration for techniques to handle high data rates.
The example leverages React hooks for state management and lifecycle handling to ensure charts are only rendered after full initialization. It follows best practices for asynchronous data fetching, handling errors with user notifications directly on the chart.








