Skip to main content

Layout: panes, controls and PaneGroup

SciFinanceChart doesn't create any DOM itself. It asks a layout for chart panes through the IChartPaneLayoutApi interface. SciFinanceChartPanel and <SciFinanceChartView> create one for you, SciChartPaneSplitter, and pass its options through: this page covers those options, and the layout underneath for when you want to build your own.

The layout has three layers, from highest to lowest:

LayerAPIRole
Layout componentSciChartPaneSplitter (React: inside <SciFinanceChartView>)The DOM: panes, drag dividers, per-pane control bars, the strip that restores hidden panes, loading placeholders
PaneGroupfinanceChartPanel.paneGroupApi, or SciChartPaneApi.createPaneManager()Headless pane state: order, sizes, visibility, direction, maximise
DashboardApiinternalSciChart surfaces: creation, axis sync, vertical and horizontal groups

Multi-chart and sub-chart modes​

Every layout supports two modes, selected by useSubCharts:

  • Sub-chart (true, the default): all panes are sub-surfaces of a single SciChartSurface. There is one WebGL context and one render loop, which is the fastest option with many panes.
  • Multi-chart (false): each pane is an independent SciChartSurface.

To switch modes, rebuild: in plain TypeScript, cleanup() and create again; in React, change the key. Save the state first to keep the studies and layout (Saving and restoring).

The Multi-Pane Layout example below switches between sub-chart and multi-chart modes, stacked and side by side, toggles axis sync, adds a custom pane, and switches the touch layout and control bar.

Pane controls​

Each pane has a control bar, shown while the pointer is over the pane. Its buttons:

  • Lock size keeps the pane at its current size while others are resized.
  • Y-axis autorange turns the pane's automatic vertical scaling on or off.
  • Move up and down (left and right when side by side).
  • Maximise the pane, or restore the others.
  • Hide the pane. It comes back from the restore strip below the panes, where each hidden pane is a pill named after its studies, such as "RSI".
  • Close the pane, detaching its studies.

Dividers between panes resize them by dragging, with the mouse or by touch. A drag skips locked panes, and carries on into the next pane once its neighbour reaches the minimum size (100 px).

paneControls sets up the control bar and dividers:

const financeChartPanel = await SciFinanceChartPanel.create(container, {
exchangeName: BinanceExchange.name, // name="Binance" matches the built-in BinanceExchange
symbol: "BTCUSDT",
interval: CandleInterval.ONE_DAY,
period: "6M",
lockPeriodWidth: false,
useSubCharts: true,
direction: "column",
paneSizing: { [PaneId.DEFAULT.value]: { grow: 4 } },
paneControls: { touch: false }, // the touch layout, for phones and tablets
studies: [new OHLCStudy(), new VolumeStudy(), new RSIStudy(), new MACDStudy(), new StochasticStudy()],
});

// Later, e.g. when the page switches to its phone layout:
financeChartPanel.setPaneControls({ touch: true });
Option (IPaneControlOptions)DefaultMeaning
visibletrueShow each pane's control bar.
touchfalseThe layout for phones and tablets. Each control bar sits in a drawer at the pane's right edge, opened by tapping or swiping its handle, because touch screens have no hover. Dividers get a wider hit area with grip dots.
buttonsall onButtons to leave out, e.g. { close: false }: lockSize, yAxisAutoRange, move, maximize, hide, close, axisSync, verticalGroup, horizontalGroup.
  • The chart doesn't detect touch screens. Your page decides, usually from its own breakpoint, and switches a live chart with setPaneControls, which changes only the options given. See Mobile and Desktop Modes.
  • Axis sync and the V-/H-group toggles are off. A finance chart keeps every pane's X axis in step itself, so it leaves those three buttons out. Turn them on with buttons: { axisSync: true, … }.
  • React. <SciFinanceChartView paneControls={…}> applies changes to the live chart, unlike config.
Multi-Pane Layout

Multi-Pane Layout

import "scichart-scitrader/scitrader.css";
import {
BinanceExchange,
CandleInterval,
defaultExchangeProvider,
idbPriceStore,
ISciFinanceChartPanelApi,
LayoutDirection,
MACDStudy,
OHLCStudy,
PaneId,
RSIStudy,
SciFinanceChartPanel,
StochasticStudy,
VolumeStudy,
} from "scichart-scitrader";

defaultExchangeProvider.registerExchange(BinanceExchange, idbPriceStore);

// <div id="toolbar"></div>
// <div id="chart" style="height: 800px"></div>
const container = document.getElementById("chart");
const toolbar = document.getElementById("toolbar");

// Hover the top of a pane for its control bar: lock size, Y-axis autorange, move, maximise, hide
// and close. Drag a divider to resize.
let paneControls = { touch: false, visible: true };

let financeChartPanel: ISciFinanceChartPanelApi = await SciFinanceChartPanel.create(container, {
exchangeName: BinanceExchange.name, // name="Binance" matches the built-in BinanceExchange
symbol: "BTCUSDT",
interval: CandleInterval.ONE_DAY,
period: "6M",
// true: every pane is a sub-chart of one SciChart surface (fastest). false: a surface per pane
useSubCharts: true,
// "column": panes stacked; "row": side by side
direction: "column",
// A price pane four times the size of each indicator pane
paneSizing: { [PaneId.DEFAULT.value]: { grow: 4 } },
paneControls,
studies: [new OHLCStudy(), new VolumeStudy(), new RSIStudy(), new MACDStudy(), new StochasticStudy()],
});

// Sub-chart mode and direction are fixed when a chart is created. To change them, rebuild it,
// carrying the studies and layout over with toJSON / fromJSON.
async function rebuild(useSubCharts: boolean, direction: LayoutDirection) {
const state = financeChartPanel.toJSON();
financeChartPanel.cleanup();
financeChartPanel = await SciFinanceChartPanel.fromJSON(
container,
{ ...state, layout: { ...state.layout, direction } },
{ useSubCharts, paneControls }
);
}

function addButton(label: string, onClick: () => void) {
const button = document.createElement("button");
button.textContent = label;
button.onclick = onClick;
toolbar.appendChild(button);
}

addButton("Sub-charts, stacked", () => rebuild(true, "column"));
addButton("Multi-chart, side by side", () => rebuild(false, "row"));

// paneGroupApi drives the same layout from code
let synced = true;
addButton("Toggle axis sync", () => {
synced = !synced;
financeChartPanel.paneGroupApi.toggleAxisSync(synced);
});

// A pane that isn't a chart, laid out with the others
addButton("Add custom pane", () => {
const content = document.createElement("div");
content.textContent = "Your content";
financeChartPanel.addCustomPane(content);
});

// The touch layout, for phones and tablets: each control bar moves into a drawer at the pane's
// right edge (tap or swipe its handle), and the dividers get a wider grip. The chart doesn't
// detect touch screens itself: your page decides, and setPaneControls switches a live chart.
addButton("Toggle touch layout", () => {
paneControls = { ...paneControls, touch: !paneControls.touch };
financeChartPanel.setPaneControls(paneControls);
});

addButton("Show / hide control bars", () => {
paneControls = { ...paneControls, visible: !paneControls.visible };
financeChartPanel.setPaneControls(paneControls);
});

Your own content in the control bar​

The control bar starts with an info area for your content, such as the SDK's candle counter. It is hidden while empty. In plain TypeScript, financeChartPanel.getPaneInfoElement(paneId) returns the element to fill; in React, paneInfo renders into it:

<SciFinanceChartView
config={config}
paneInfo={(paneId) =>
paneId === PaneId.DEFAULT.value && chart ? <CandleCounter chart={chart.financeApi} /> : null
}
/>

PaneId.DEFAULT is the price pane. Return null to leave a pane's area empty.

Loading states​

A pane shows what is happening while its data loads:

  • Until its chart is ready, each pane shows a shimmering skeleton (.st-pane-skeleton), or content of your own.
  • While a symbol or interval switch loads, each chart pane is covered by a translucent shimmer that also blocks input (.st-pane-loading-overlay).

loading (IPaneLoadingOptions) changes them:

OptionDefaultMeaning
placeholdertruetrue for the skeleton, false for nothing, or a function (paneId) => HTMLElement for your own content.
onPlaceholderRemovednoneCalled with each placeholder when its pane's chart is ready, to clean up what you put in it.
overlaytrueWhether to show the switch overlay.

Progress dots. The SDK has a placeholder that follows a pane's loading phases: three dots and a caption ("Initialising surface", "Configuring chart", "Loading data"). The chart reports the phases to a PaneProgressEmitter passed as services.progress. In React, give <SciFinanceChartView> a paneLoadingContent:

// One emitter per mounted chart: the chart reports each pane's loading phases to it.
const progress = React.useMemo(() => new PaneProgressEmitter(), [mount]);

const config = React.useMemo<ISciFinanceChartPanelConfig>(
() => ({ /* … */ services: { progress } }),
[progress]
);

<SciFinanceChartView
key={mount}
config={config}
paneLoadingContent={(paneId) => <PaneProgressDots emitter={progress} paneId={paneId} />}
/>

In plain TypeScript, loading: paneProgressDotsLoading(progress) does the same, and createPaneProgressDots(emitter, paneId) gives one pane's dots for a placeholder of your own.

Loading States and Pane Info

Loading States and Pane Info

import "scichart-scitrader/scitrader.css";
import {
BinanceExchange,
CandleCounter,
CandleInterval,
defaultExchangeProvider,
idbPriceStore,
IntervalSelector,
OHLCStudy,
PaneId,
PaneProgressEmitter,
paneProgressDotsLoading,
RSIStudy,
SciFinanceChartPanel,
VolumeStudy,
} from "scichart-scitrader";

defaultExchangeProvider.registerExchange(BinanceExchange, idbPriceStore);

// <div id="toolbar"></div>
// <div id="chart" style="height: 600px"></div>

// The chart reports each pane's loading phases (surface → configured → data) to this emitter.
// Pass it in `services.progress` so it hears them from the start, before create() resolves.
const progress = new PaneProgressEmitter();

const financeChartPanel = await SciFinanceChartPanel.create(document.getElementById("chart"), {
exchangeName: BinanceExchange.name, // name="Binance" matches the built-in BinanceExchange
symbol: "BTCUSDT",
interval: CandleInterval.FIVE_MINUTES,
period: "1D",
studies: [new OHLCStudy(), new VolumeStudy(), new RSIStudy()],
services: { progress },
loading: {
// Progress dots in each pane until its chart is ready, in place of the default skeleton
...paneProgressDotsLoading(progress),
// The shimmer over the panes while a symbol or interval change loads (on by default)
overlay: true,
},
});
const chart = financeChartPanel.financeApi; // SciFinanceChart

// Your own content at the start of a pane's control bar (hover the price pane to see it):
// here, the SDK's candle counter, "visible / loaded / ~total"
const counter = CandleCounter.create(financeChartPanel.getPaneInfoElement(PaneId.DEFAULT.value), { chart });

// Changing the interval covers the panes with the shimmer until the new candles are drawn
IntervalSelector.create(document.getElementById("toolbar"), {
value: CandleInterval.FIVE_MINUTES,
intervals: [CandleInterval.ONE_MINUTE, CandleInterval.FIVE_MINUTES, CandleInterval.ONE_HOUR],
onChange: (interval) => chart.changeInterval(interval),
});

// When the page goes away:
// counter.destroy(); financeChartPanel.cleanup();

IChartPaneLayoutApi​

This is what a layout implements to host a SciFinanceChart:

MethodCalled when
createChartPane(config) → Promise<SciChartSurface>A study needs a new pane
removeChartPane(id)A pane's last study is detached
setExpanded(id | null) / isExpanded(id)Maximising a pane, or restoring all with null
syncAxis(id, config?) / unsyncAxis(id)Keeping X-axes in step across panes
addToVerticalGroup(id, groupId?) / removeFromVerticalGroup(id)Aligning Y-axis widths so the plot areas line up
addToHorizontalGroup(id, groupId?) / removeFromHorizontalGroup(id)Aligning panes placed side by side
setAxisLabelsVisible(id, visible)Showing X-axis labels only on the bottom pane

SciChartPaneSplitter implements it. To host the chart in a different layout, implement it yourself, then use SciFinanceChart.createWithStudies(yourLayoutApi, options) directly.

SciChartPaneSplitter​

import { SciChartPaneSplitter } from "scichart-scitrader";

const splitter = await SciChartPaneSplitter.create(container, {
panes: [], // SciFinanceChart adds chart panes itself
direction: "column",
useSubCharts: true,
createSuspended: true, // let the chart resume rendering once everything is attached
paneControls: { touch: false },
});

const chart = await SciFinanceChart.createWithStudies(splitter, { exchangeName, symbol, interval, studies });

This is what SciFinanceChartPanel.create does. The splitter provides the dividers, the control bars, the restore strip and the loading states described above. A splitter used directly shows every control-bar button, including axis sync and the V-/H-group toggles.

splitter.group is its PaneGroup, and splitter.addCustomPane() adds a pane that isn't a chart.

PaneGroup​

Pane Sizes and Pane State shows the common tasks, with live examples: sizing panes, and maximising, hiding, closing and autoranging them in code.

PaneGroup is the single source of truth for layout state. The splitter reads snapshot(), renders it, and changes the layout only through PaneGroup's methods. PaneGroup then re-applies the chart rules itself, including axis labels on the bottom pane only, group membership, axis sync and chart visibility.

const group = financeChartPanel.paneGroupApi;

group.setDirection("row");
group.reorderPane(paneId, 0);
group.setPaneSizes([{ id: "default", size: 600 }]);
group.lockPaneSize(paneId, 120); // fixed height in pixels
group.toggleExpanded(paneId); // maximise / restore
group.setVisible(paneId, false);
group.toggleAxisSync(true); // X-axes in step across every pane

group.stateChanged.subscribe((snapshot) => render(snapshot));

API surface

GroupMethods and events
Lifecycleinit, addCustomPane, getChartInitializer, removePane, cleanup
Order and layoutreorderPane, swapPanes, setDirection
VisibilitysetVisible, toggleVisible, setExpanded, toggleExpanded
SizingsetPaneSize, setPaneSizes, lockPaneSize, unlockPaneSize
CoordinationtoggleAxisSync, syncAxis, unsyncAxis, add/removeFromVerticalGroup, add/removeFromHorizontalGroup, setAxisLabelsVisible
QueriesgetPane, getAllPanes, getChart, isAxisSynced, snapshot
PersistencetoJSON({ includeChartCoordination }), fromJSON
EventsstateChanged, paneAdded, paneRemoved, orderChanged, visibilityChanged, expandedChanged, directionChanged, sizeChanged, axisSyncChanged, verticalGroupChanged, horizontalGroupChanged, allPanesInitialized

SciChartPaneApi.createPaneManager() creates a multi-chart group, and createSubChartPaneManager() a sub-chart group. The sub-chart group adds setContainerElement and recalculateSubChartPositions.

When saving a finance chart, use toJSON({ includeChartCoordination: false }), which SciFinanceChartPanel.toJSON already does: SciFinanceChart rebuilds the surfaces itself. The default, true, also serialises SciChart surface state, for layouts of plain charts.

Under PaneGroup​

PaneGroup creates and coordinates the SciChart surfaces through an internal layer, SciChartDashboardApi. It handles surface creation, axis sync, and the vertical and horizontal groups that line up axes. It isn't exported: use PaneGroup, which offers the same coordination with pane state on top.

Logger (Logger.enable(context)) traces the layout and data layers when debugging.

Notes​

  • Element ids must be unique. SciChart tracks surfaces by their element's id, so two surfaces in elements with the same id collide, and creating the second silently deletes the first. The SDK gives an element without an id a unique one; ids you set yourself must not repeat.
  • Sub-chart mode stacks DOM layers. The parent surface sits behind (z-index: 0). The flex container of panes sits on top (z-index: 1) with pointer-events: none, so mouse and touch reach the chart.
  • Legacy classes. PaneLayoutManager, PaneManager and LayoutManager are superseded by the classes on this page and are not part of the public API.