Demonstrates how to create multiple synchronized subcharts with an interactive overview in a React application using SciChart.js
drawExample.ts
index.tsx
AxisSynchroniser.ts
theme.ts
SubChartsOverviewModifier.ts
1import {
2 SciChartSurface,
3 NumericAxis,
4 NumberRange,
5 FastLineRenderableSeries,
6 XyDataSeries,
7 ZoomExtentsModifier,
8 MouseWheelZoomModifier,
9 ZoomPanModifier,
10 ESubSurfacePositionCoordinateMode,
11 Rect,
12 EPerformanceMarkType,
13 SciChartSubSurface,
14 I2DSubSurfaceOptions,
15 EAutoRange,
16 RolloverModifier,
17 EXyDirection,
18} from "scichart";
19
20import { appTheme } from "../../../theme";
21import { SubChartsOverviewModifier } from "./SubChartsOverviewModifier";
22import { AxisSynchroniser } from "../../MultiChart/SyncMultiChart/AxisSynchroniser";
23
24const colorsArr = [
25 appTheme.MutedBlue,
26 appTheme.MutedOrange,
27 appTheme.MutedPink,
28 appTheme.MutedPurple,
29 appTheme.MutedRed,
30 appTheme.MutedSkyBlue,
31 appTheme.MutedTeal,
32];
33
34export type TMarkType = EPerformanceMarkType | string;
35
36export interface SubChartConfig {
37 id: string;
38 phase: number;
39 color: string;
40 title: string;
41}
42
43export interface SubChartManager {
44 updateSubCharts: (configs: SubChartConfig[]) => void;
45 addSubChart: (config: SubChartConfig) => void;
46 removeSubChart: (id: string) => void;
47 updateLayout: () => void;
48}
49
50// Helper to create some sample data
51const createLineData = (phase: number) => {
52 const xValues: number[] = [];
53 const yValues: number[] = [];
54 for (let i = 0; i < 500; i++) {
55 const x = i;
56 const y = Math.sin(i * 0.1 + phase);
57 xValues.push(x);
58 yValues.push(y);
59 }
60 return { xValues, yValues };
61};
62
63export const drawExample = async (
64 rootElement: string | HTMLDivElement,
65 initialConfigs: SubChartConfig[]
66): Promise<{ wasmContext: any; sciChartSurface: SciChartSurface; manager: SubChartManager }> => {
67 // Create a SciChartSurface
68 const { wasmContext, sciChartSurface } = await SciChartSurface.create(rootElement);
69
70 // Store references to subcharts for dynamic management
71 const subChartMap = new Map<string, SciChartSubSurface>();
72 const axisSynchroniser = new AxisSynchroniser(new NumberRange(0, 500));
73
74 // Add main axes to the surface for the overview to reference
75 const mainXAxis = new NumericAxis(wasmContext, {
76 id: "mainXAxis",
77 isVisible: false, // Hidden since subcharts have their own axes
78 autoRange: EAutoRange.Always,
79 });
80 const mainYAxis = new NumericAxis(wasmContext, {
81 id: "mainYAxis",
82 isVisible: false, // Hidden since subcharts have their own axes
83 autoRange: EAutoRange.Always,
84 });
85
86 sciChartSurface.xAxes.add(mainXAxis);
87 sciChartSurface.yAxes.add(mainYAxis);
88
89 // Create overview modifier
90 const overviewModifier = new SubChartsOverviewModifier({
91 overviewPosition: new Rect(0, 0.8, 1, 0.2),
92 isTransparent: true,
93 axisTitle: "Overview - All Charts",
94 labelStyle: {
95 color: "#ffffff80",
96 fontSize: 10,
97 },
98 majorTickLineStyle: {
99 color: "#ffffff80",
100 tickSize: 6,
101 strokeThickness: 1,
102 },
103 yAxisGrowBy: new NumberRange(0.1, 0.1),
104 });
105
106 sciChartSurface.chartModifiers.add(overviewModifier);
107
108 const updateLayout = () => {
109 // Simple layout update without recreating subcharts
110 // This is called after add/remove operations to ensure proper spacing
111 // The actual repositioning will be handled by the individual add/remove functions
112 };
113
114 const addSubChart = (config: SubChartConfig, index?: number) => {
115 // Don't add if already exists
116 if (subChartMap.has(config.id)) {
117 return;
118 }
119
120 // For now, just add at the bottom to avoid repositioning existing charts
121 const currentCount = subChartMap.size;
122 const chartIndex = currentCount; // Always add at the end
123
124 // Calculate position: this is a simple approach that may cause overlapping
125 // but avoids the MouseManager issues
126 const rect = new Rect(0, chartIndex * 0.2, 1, 0.2);
127
128 const subChartOptions: I2DSubSurfaceOptions = {
129 id: config.id,
130 position: rect,
131 coordinateMode: ESubSurfacePositionCoordinateMode.Relative,
132 };
133
134 const subChart = SciChartSubSurface.createSubSurface(sciChartSurface, subChartOptions);
135
136 // Create axes for the subchart
137 const subXAxis = new NumericAxis(wasmContext);
138 const subYAxis = new NumericAxis(wasmContext, {
139 growBy: new NumberRange(0.1, 0.1),
140 axisTitle: config.title,
141 axisTitleStyle: { fontSize: 14 },
142 drawMinorGridLines: false,
143 });
144
145 subChart.xAxes.add(subXAxis);
146 subChart.yAxes.add(subYAxis);
147
148 // Create data and series
149 const data = createLineData(config.phase);
150 const dataSeries = new XyDataSeries(wasmContext, {
151 xValues: data.xValues,
152 yValues: data.yValues,
153 });
154
155 const lineSeries = new FastLineRenderableSeries(wasmContext, {
156 dataSeries,
157 strokeThickness: 4,
158 stroke: config.color,
159 opacity: 0.6,
160 });
161
162 // Add to synchronizer and subchart
163 axisSynchroniser.addAxis(subXAxis);
164 subChart.renderableSeries.add(lineSeries);
165
166 // Add modifiers
167 subChart.chartModifiers.add(
168 new ZoomPanModifier(),
169 new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
170 new ZoomExtentsModifier(),
171 new RolloverModifier({ modifierGroup: "one" })
172 );
173
174 // Store reference
175 subChartMap.set(config.id, subChart);
176
177 subChart.zoomExtents();
178 };
179
180 const removeSubChart = (id: string) => {
181 const subChart = subChartMap.get(id);
182 if (subChart) {
183 // Remove from synchronizer
184 const xAxis = subChart.xAxes.get(0);
185 if (xAxis) {
186 axisSynchroniser.removeAxis(xAxis);
187 }
188
189 // Use removeSubChart on the parent surface to properly remove from subChartsProperty array
190 // This prevents MouseManager from iterating over deleted subcharts
191 sciChartSurface.removeSubChart(subChart);
192 subChartMap.delete(id);
193 }
194 };
195
196 const updateSubChart = (id: string, config: SubChartConfig) => {
197 const subChart = subChartMap.get(id);
198 if (subChart) {
199 // Update title
200 const yAxis = subChart.yAxes.get(0);
201 if (yAxis) {
202 yAxis.axisTitle = config.title;
203 }
204
205 // Update color
206 const series = subChart.renderableSeries.get(0) as FastLineRenderableSeries;
207 if (series) {
208 series.stroke = config.color;
209 }
210
211 // Update data if phase changed
212 const data = createLineData(config.phase);
213 if (series && series.dataSeries) {
214 (series.dataSeries as XyDataSeries).clear();
215 (series.dataSeries as XyDataSeries).appendRange(data.xValues, data.yValues);
216 }
217 }
218 };
219
220 const updateSubCharts = (configs: SubChartConfig[]) => {
221 // Use the safer recreate approach to avoid MouseManager issues
222 recreateSubChartsWithLayout(configs);
223 };
224
225 const recreateSubChartsWithLayout = (configs: SubChartConfig[]) => {
226 // Suspend updates to prevent MouseManager from iterating over partially deleted subcharts
227 sciChartSurface.suspendUpdates();
228
229 try {
230 // Clear existing subcharts - use the proper removeSubChart function to ensure cleanup
231 const currentIds = Array.from(subChartMap.keys());
232 currentIds.forEach((id) => {
233 removeSubChart(id);
234 });
235
236 // Recreate with proper layout
237 const count = configs.length;
238 if (count === 0) {
239 sciChartSurface.resumeUpdates();
240 return;
241 }
242
243 configs.forEach((config, index) => {
244 // Calculate position: each subchart takes 1/count of the available 80% height
245 // Y position starts at (index/count) * 0.8 and has height of (1/count) * 0.8
246 const yStart = (index / count) * 0.8;
247 const height = (1 / count) * 0.8;
248 const rect = new Rect(0, yStart, 1, height);
249
250 const subChartOptions: I2DSubSurfaceOptions = {
251 id: config.id,
252 position: rect,
253 coordinateMode: ESubSurfacePositionCoordinateMode.Relative,
254 };
255
256 const subChart = SciChartSubSurface.createSubSurface(sciChartSurface, subChartOptions);
257
258 // Create axes for the subchart
259 const subXAxis = new NumericAxis(wasmContext);
260 const subYAxis = new NumericAxis(wasmContext, {
261 growBy: new NumberRange(0.1, 0.1),
262 axisTitle: config.title,
263 axisTitleStyle: { fontSize: 14 },
264 drawMinorGridLines: false,
265 });
266
267 subChart.xAxes.add(subXAxis);
268 subChart.yAxes.add(subYAxis);
269
270 // Create data and series
271 const data = createLineData(config.phase);
272 const dataSeries = new XyDataSeries(wasmContext, {
273 xValues: data.xValues,
274 yValues: data.yValues,
275 });
276
277 const lineSeries = new FastLineRenderableSeries(wasmContext, {
278 dataSeries,
279 strokeThickness: 4,
280 stroke: config.color,
281 opacity: 0.6,
282 });
283
284 // Add to synchronizer and subchart
285 axisSynchroniser.addAxis(subXAxis);
286 subChart.renderableSeries.add(lineSeries);
287
288 // Add modifiers
289 subChart.chartModifiers.add(
290 new ZoomPanModifier(),
291 new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
292 new ZoomExtentsModifier(),
293 new RolloverModifier({ modifierGroup: "one" })
294 );
295
296 // Store reference
297 subChartMap.set(config.id, subChart);
298
299 subChart.zoomExtents();
300 });
301 } finally {
302 sciChartSurface.resumeUpdates();
303 }
304 };
305
306 // Initialize with provided configs
307 initialConfigs.forEach((config) => addSubChart(config));
308
309 sciChartSurface.zoomExtents();
310
311 const manager: SubChartManager = {
312 updateSubCharts,
313 addSubChart,
314 removeSubChart,
315 updateLayout,
316 };
317
318 return { wasmContext, sciChartSurface, manager };
319};
320This example demonstrates how to implement a multi-subchart layout with a shared overview in a React application using SciChart.js. Multiple vertically stacked charts are rendered within a single SciChartSurface, each synchronized on the X-axis and controlled through an interactive overview panel.
The chart is initialized via the <SciChartReact /> component, which invokes a custom drawExample function. This function dynamically creates several SciChartSubSurface instances, each configured with its own axes, renderable series, and interaction modifiers. An AxisSynchroniser ensures all subcharts maintain the same visible X-range.
The overview functionality is encapsulated in a reusable SubChartsOverviewModifier, which listens for subchart lifecycle events and mirrors their renderable series into an overview subsurface. The OverviewRangeSelectionModifier allows users to control zoom and pan interactions across all subcharts from a single control surface.
React-Friendly Lifecycle Management: Subcharts are safely created and destroyed without React reconciliation conflicts by leveraging SciChart’s internal update suspension mechanisms.
Centralized Zoom Control: Users can zoom and pan all subcharts simultaneously using either direct mouse interaction or the overview range selector.
Reusable Overview Modifier: The overview logic is encapsulated in a custom chart modifier, making it easy to reuse across different dashboards or chart configurations.
High-Performance Real-Time Charts: The example showcases how SciChart.js integrates seamlessly into React while maintaining WebAssembly-powered performance.
This approach follows best practices for integrating SciChart.js into React by isolating chart creation logic from React rendering. Developers can extend this pattern to support real-time streaming data, dynamic chart layouts, or advanced dashboard interactions. For more guidance, see Creating a SciChart React Component and the React Charts with SciChart.js guide.

Demonstrates Multiple X & Y Axis on a React Chart using SciChart.js. SciChart supports unlimited left, right, top, bottom X, Y axis with configurable alignment and individual zooming, panning

Demonstrates Secondary Y Axis on a React Chart using SciChart.js. SciChart supports unlimited, multiple left, right, top, bottom X, Y axis with configurable alignment and individual zooming, panning

Demonstrates how to Zoom, Scale or Pan individual Axis on a React Chart with SciChart.js AxisDragModifiers

Demonstrates how to zoom and pan a realtime React Chart while it is updating, with SciChart.js ZoomState API

Demonstrates how to use multiple Zoom and Pan Modifiers on a React Chart with SciChart.js

Demonstrates how to zoom and pan with an Overview Chart

shows how to load data on zoom/pan and how to create an overview chart for this case.

Explore SciChart's Polar Interactivity Modifiers including zooming, panning, and cursor tracking. Try the demo to trial the Polar Chart Behavior Modifiers.

Demonstrates 64-bit precision Date Axis in SciChart.js handling Nanoseconds to Billions of Years