React Overview for SubCharts with Range Selection

Demonstrates how to create multiple synchronized subcharts with an interactive overview in a React application using SciChart.js

4

Fullscreen

Edit

 Edit

Docs

drawExample.ts

index.tsx

AxisSynchroniser.ts

theme.ts

SubChartsOverviewModifier.ts

Copy to clipboard
Minimise
Fullscreen
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    EXyDirection,
17    CursorModifier,
18    ENumericFormat,
19    SeriesInfo,
20    CursorTooltipSvgAnnotation,
21} from "scichart";
22import { appTheme } from "../../../theme";
23import { SubChartsOverviewModifier } from "./SubChartsOverviewModifier";
24import { AxisSynchroniser } from "../../MultiChart/SyncMultiChart/AxisSynchroniser";
25
26const STREAM_INTERVAL_MS = 1000;
27const POINTS_ON_CHART = 180;
28const OVERVIEW_HEIGHT = 0.2;
29const SUBCHARTS_HEIGHT = 1 - OVERVIEW_HEIGHT;
30
31export type TMarkType = EPerformanceMarkType | string;
32
33export interface SubChartConfig {
34    id: string;
35    phase: number;
36    color: string;
37    title: string;
38}
39
40export interface SubChartManager {
41    updateSubCharts: (configs: SubChartConfig[]) => void;
42    addSubChart: (config: SubChartConfig) => void;
43    removeSubChart: (id: string) => void;
44    updateLayout: () => void;
45}
46
47interface SubChartRuntime {
48    subChart: SciChartSubSurface;
49    dataSeries: XyDataSeries;
50    phase: number;
51    color: string;
52    title: string;
53}
54
55const getYValue = (x: number, phase: number): number =>
56    Math.sin(x * 0.08 + phase) * 0.75 + Math.cos(x * 0.015 + phase * 0.5) * 0.25;
57
58const createLineData = (phase: number, startX: number, count: number) => {
59    const xValues: number[] = [];
60    const yValues: number[] = [];
61    for (let i = 0; i < count; i++) {
62        const x = startX + i;
63        xValues.push(x);
64        yValues.push(getYValue(x, phase));
65    }
66    return { xValues, yValues };
67};
68
69const getTooltipLegendTemplate = (seriesInfos: SeriesInfo[], svgAnnotation: CursorTooltipSvgAnnotation) => {
70    const legendItems = seriesInfos.filter((si) => si.isWithinDataBounds);
71    if (!legendItems.length) {
72        return "<svg width='100%' height='100%'></svg>";
73    }
74
75    const lineHeight = 18;
76    const padding = 8;
77    const titleY = padding + 12;
78    const legendHeight = padding * 2 + 12 + lineHeight * legendItems.length;
79    const currentTime = legendItems[0].formattedXValue;
80
81    let rows = `<text x="${padding}" y="${titleY}" font-size="12" font-family="Verdana" fill="#d4d7dd">Time: ${currentTime}</text>`;
82    legendItems.forEach((seriesInfo, index) => {
83        const y = titleY + 8 + (index + 1) * lineHeight;
84        const seriesColor = seriesInfo.stroke || "#E8C667";
85        rows += `<text x="${padding}" y="${y}" font-size="12" font-family="Verdana" fill="${seriesColor}">${seriesInfo.seriesName}: ${seriesInfo.formattedYValue}</text>`;
86    });
87
88    return `<svg width="100%" height="${legendHeight}">
89        <rect x="1" y="1" width="98%" height="${
90            legendHeight - 2
91        }" rx="4" ry="4" fill="#0D1523CC" stroke="#E8C667AA" stroke-width="1"/>
92        ${rows}
93    </svg>`;
94};
95
96export const drawExample = async (
97    rootElement: string | HTMLDivElement,
98    initialConfigs: SubChartConfig[]
99): Promise<{ wasmContext: any; sciChartSurface: SciChartSurface; manager: SubChartManager }> => {
100    const { wasmContext, sciChartSurface } = await SciChartSurface.create(rootElement);
101
102    const subChartMap = new Map<string, SciChartSubSurface>();
103    const subChartRuntimeMap = new Map<string, SubChartRuntime>();
104
105    let nextStreamX = Math.floor(Date.now() / 1000) + 1;
106    const initialVisibleRange = new NumberRange(nextStreamX - POINTS_ON_CHART, nextStreamX - 1);
107    const axisSynchroniser = new AxisSynchroniser(initialVisibleRange);
108
109    const mainXAxis = new NumericAxis(wasmContext, {
110        id: "mainXAxis",
111        isVisible: false,
112        autoRange: EAutoRange.Always,
113    });
114    const mainYAxis = new NumericAxis(wasmContext, {
115        id: "mainYAxis",
116        isVisible: false,
117        autoRange: EAutoRange.Always,
118    });
119
120    sciChartSurface.xAxes.add(mainXAxis);
121    sciChartSurface.yAxes.add(mainYAxis);
122
123    const overviewModifier = new SubChartsOverviewModifier({
124        overviewPosition: new Rect(0, SUBCHARTS_HEIGHT, 1, OVERVIEW_HEIGHT),
125        isTransparent: true,
126        axisTitle: "Overview - All Charts",
127        labelStyle: {
128            color: "#ffffff80",
129            fontSize: 10,
130        },
131        majorTickLineStyle: {
132            color: "#ffffff80",
133            tickSize: 6,
134            strokeThickness: 1,
135        },
136        yAxisGrowBy: new NumberRange(0.1, 0.1),
137    });
138
139    sciChartSurface.chartModifiers.add(overviewModifier);
140
141    const getCurrentConfigs = (): SubChartConfig[] =>
142        Array.from(subChartRuntimeMap.entries()).map(([id, runtime]) => ({
143            id,
144            phase: runtime.phase,
145            color: runtime.color,
146            title: runtime.title,
147        }));
148
149    const createCursorModifier = () =>
150        new CursorModifier({
151            modifierGroup: "subcharts-cursor",
152            showAxisLabels: true,
153            showTooltip: true,
154            isSvgOnly: false,
155            crosshairStroke: appTheme.ForegroundColor,
156            crosshairStrokeThickness: 1,
157            axisLabelFill: appTheme.ForegroundColor,
158            axisLabelStroke: "#0D1523",
159            tooltipContainerBackground: "#0D1523",
160            tooltipTextStroke: appTheme.ForegroundColor,
161            tooltipLegendTemplate: getTooltipLegendTemplate,
162        });
163
164    const createSubChart = (config: SubChartConfig, rect: Rect) => {
165        const subChartOptions: I2DSubSurfaceOptions = {
166            id: config.id,
167            position: rect,
168            coordinateMode: ESubSurfacePositionCoordinateMode.Relative,
169        };
170
171        const subChart = SciChartSubSurface.createSubSurface(sciChartSurface, subChartOptions);
172
173        const subXAxis = new NumericAxis(wasmContext, {
174            autoRange: EAutoRange.Always,
175            labelFormat: ENumericFormat.Date_HHMMSS,
176            cursorLabelFormat: ENumericFormat.Date_HHMMSS,
177            drawMinorGridLines: false,
178            maxAutoTicks: 6,
179        });
180
181        const subYAxis = new NumericAxis(wasmContext, {
182            autoRange: EAutoRange.Always,
183            growBy: new NumberRange(0.1, 0.1),
184            axisTitle: config.title,
185            axisTitleStyle: { fontSize: 14 },
186            drawMinorGridLines: false,
187        });
188
189        subChart.xAxes.add(subXAxis);
190        subChart.yAxes.add(subYAxis);
191
192        const data = createLineData(config.phase, nextStreamX - POINTS_ON_CHART, POINTS_ON_CHART);
193        const dataSeries = new XyDataSeries(wasmContext, {
194            xValues: data.xValues,
195            yValues: data.yValues,
196            dataSeriesName: config.title,
197        });
198
199        const lineSeries = new FastLineRenderableSeries(wasmContext, {
200            dataSeries,
201            strokeThickness: 3,
202            stroke: config.color,
203            opacity: 0.7,
204        });
205
206        axisSynchroniser.addAxis(subXAxis);
207        subChart.renderableSeries.add(lineSeries);
208
209        subChart.chartModifiers.add(
210            new ZoomPanModifier(),
211            new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
212            new ZoomExtentsModifier(),
213            createCursorModifier()
214        );
215
216        subChartMap.set(config.id, subChart);
217        subChartRuntimeMap.set(config.id, {
218            subChart,
219            dataSeries,
220            phase: config.phase,
221            color: config.color,
222            title: config.title,
223        });
224
225        subChart.zoomExtents();
226    };
227
228    const clearAllSubCharts = () => {
229        const currentSubCharts = Array.from(subChartMap.values());
230        currentSubCharts.forEach((subChart) => {
231            const xAxis = subChart.xAxes.get(0);
232            if (xAxis) {
233                axisSynchroniser.removeAxis(xAxis);
234            }
235            sciChartSurface.removeSubChart(subChart);
236        });
237        subChartMap.clear();
238        subChartRuntimeMap.clear();
239    };
240
241    const recreateSubChartsWithLayout = (configs: SubChartConfig[]) => {
242        sciChartSurface.suspendUpdates();
243        try {
244            clearAllSubCharts();
245
246            if (configs.length === 0) {
247                return;
248            }
249
250            configs.forEach((config, index) => {
251                const yStart = (index / configs.length) * SUBCHARTS_HEIGHT;
252                const height = (1 / configs.length) * SUBCHARTS_HEIGHT;
253                createSubChart(config, new Rect(0, yStart, 1, height));
254            });
255        } finally {
256            sciChartSurface.resumeUpdates();
257        }
258    };
259
260    const updateSubCharts = (configs: SubChartConfig[]) => {
261        recreateSubChartsWithLayout(configs);
262    };
263
264    const addSubChart = (config: SubChartConfig) => {
265        if (subChartMap.has(config.id)) {
266            return;
267        }
268        recreateSubChartsWithLayout([...getCurrentConfigs(), config]);
269    };
270
271    const removeSubChart = (id: string) => {
272        if (!subChartMap.has(id)) {
273            return;
274        }
275        recreateSubChartsWithLayout(getCurrentConfigs().filter((cfg) => cfg.id !== id));
276    };
277
278    const updateLayout = () => {
279        recreateSubChartsWithLayout(getCurrentConfigs());
280    };
281
282    recreateSubChartsWithLayout(initialConfigs);
283    sciChartSurface.zoomExtents();
284
285    const streamInterval = window.setInterval(() => {
286        subChartRuntimeMap.forEach((runtime) => {
287            runtime.dataSeries.append(nextStreamX, getYValue(nextStreamX, runtime.phase));
288            const excess = runtime.dataSeries.count() - POINTS_ON_CHART;
289            if (excess > 0) {
290                runtime.dataSeries.removeRange(0, excess);
291            }
292        });
293
294        nextStreamX += 1;
295    }, STREAM_INTERVAL_MS);
296
297    sciChartSurface.addDeletable({
298        delete: () => window.clearInterval(streamInterval),
299    });
300
301    const manager: SubChartManager = {
302        updateSubCharts,
303        addSubChart,
304        removeSubChart,
305        updateLayout,
306    };
307
308    return { wasmContext, sciChartSurface, manager };
309};
310

Overview for SubCharts – React

Overview

This 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.

Technical Implementation

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.

Features and Capabilities

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.

Integration and Best Practices

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.

react Chart Examples & Demos

See Also: Charts added in v5 (9 Demos)

NEW!
React Trading Drawing Tools | React Charts | SciChart.js

React Trading Drawing Tools

Create an interactive React trading charts for technical analysis. Trading Drawing Tools Demo, which shows how to use Polylines, Extended Lines, Rays, Channels, Pitchforks, Pitchfans, Fibonnaci Retracements, Measure, Stop Loss and Take Profit chart drawing tools for Technical Analysis.

NEW!
React Freehand Drawing Tools | React Charts | SciChart.js

React Freehand Drawing Tools

An example of using React FreehandDrawingModifier for arbitrary drawing on trading and financial charts. Can be used for drawing trends, arrow, markers, text, etc.

NEW!
React  Smith Chart | React Charts | SciChart.js Demo

React Smith Chart

Interactive React Smith chart for RF impedance matching — place markers, build matching networks step by step with the component chain, and switch between impedance and admittance grids.

NEW!
High Performance SVG Cursor & Rollover | SciChart.js Demo

High Performance SVG Cursor & Rollover

Demonstrates how to use the SVG render layer in SciChart.js to maintain smooth cursor interaction on heavy charts with millions of points.

NEW!
React Force Directed Graph | React Charts | SciChart.js

React Force Directed Graph

React Force Directed Graph demo by SciChart.js. Visualize network graphs with physics simulation, interactive node dragging, and hover tooltips.

NEW!
React Orderbook Heatmap | React Charts | SciChart.js Demo

Order Book Heatmap

Create a React heatmap chart showing historical orderbook levels, using the high performance SciChart.js chart library. Get free demo now.

NEW!
High Precision Date Axis | React Charts | SciChart.js Demo

High Precision Date Axis

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

NEW!
React Chart with DiscontinuousDateAxis Comparison | SciChart

DiscontinuousDateAxis Comparison with React

NEW!
React Chart with BaseValue Axes | React Charts | SciChart.js

React Chart with BaseValue Axes

Demonstrates BaseValue Axes on a React Chart using SciChart.js to create non-linear and custom-scaled axes

SciChart Ltd, 16 Beaufort Court, Admirals Way, Docklands, London, E14 9XL.