Creates a React Waterfall Chart using SciChart.js's new FastRectangleRenderableSeries with the following features: a custom Treemap-like DataLabelProvider for rectangle labels and custom Fill PaletteProvider
Also Known As: Bridge Chart and Cascade Chart.
import {
NumericAxis,
ZoomPanModifier,
ZoomExtentsModifier,
MouseWheelZoomModifier,
SciChartSurface,
ENumericFormat,
FastRectangleRenderableSeries,
XyxyDataSeries,
EColumnYMode,
EColumnMode,
EDataPointWidthMode,
NumberRange,
EHorizontalTextPosition,
EVerticalTextPosition,
TextLabelProvider,
RectangleSeriesDataLabelProvider,
formatNumber,
IRectangleSeriesDataLabelProviderOptions,
EDataLabelSkipMode,
EMultiLineAlignment,
IPointMetadata,
IFillPaletteProvider,
EFillPaletteMode,
parseColorToUIntArgb,
ELabelAlignment,
Thickness,
} from "scichart";
import { appTheme } from "./appTheme";
const data = [
{ date: "Jan.", profit: 387264 },
{ date: "Feb.", profit: 772096 },
{ date: "Mar.", profit: 638075 },
{ date: "Apr.", profit: -211386 },
{ date: "May", profit: -138135 },
{ date: "Jun.", profit: -267238 },
{ date: "Jul.", profit: 431406 },
{ date: "Aug.", profit: 363018 },
{ date: "Sep.", profit: -224638 },
{ date: "Oct.", profit: -299867 },
{ date: "Nov.", profit: 607365 },
{ date: "Dec.", profit: 1106986 },
];
const waterfall = function (numbers: { date: string; profit: number }[]) {
let last = 0,
accu = 0;
const waterfall = numbers.map((d, i) => {
last = accu;
accu += d.profit;
return {
date: d.date,
nextDay: i < numbers.length - 1 ? numbers[i + 1].date : "Total",
prior: last,
accu: accu,
profit: d.profit,
};
});
waterfall.push({
date: "Total",
nextDay: null,
prior: 0,
accu: accu,
profit: 0,
});
return waterfall;
};
const generatedData = waterfall(data);
const labelProvider = new TextLabelProvider({
labels: [...generatedData.map((d) => d.date)],
maxLength: 15,
});
export const drawExample = async (rootElement: string | HTMLDivElement) => {
// Create a SciChartSurface
const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
theme: appTheme.SciChartJsTheme,
});
const growBy = new NumberRange(0.05, 0.05);
// Create an XAxis with a TextLabelProvider
const xAxis = new NumericAxis(wasmContext, {
// labelprovider maps Xaxis values (in this case index) to labels (in this case manufacturer name)
labelProvider,
labelStyle: {
alignment: ELabelAlignment.Center,
padding: new Thickness(2, 1, 2, 1),
fontSize: 11,
},
maxAutoTicks: 15,
growBy: new NumberRange(0.05, 0.05), // add some horizontal padding
});
sciChartSurface.xAxes.add(xAxis);
// Create a YAxis
sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { growBy, labelFormat: ENumericFormat.Engineering }));
class CustomFillProvider implements IFillPaletteProvider {
public readonly fillPaletteMode = EFillPaletteMode.SOLID;
public onAttached(): void {}
public onDetached(): void {}
public overrideFillArgb(
_xValue: number,
_yValue: number,
_index: number,
_opacity?: number,
metadata?: IPointMetadata
): number {
const profit = (metadata as unknown as { profit: number })?.profit;
if (profit > 0) {
return parseColorToUIntArgb(appTheme.VividTeal, 255);
}
if (profit < 0) {
return parseColorToUIntArgb(appTheme.VividPink, 255);
}
return parseColorToUIntArgb(appTheme.VividBlue, 128);
}
}
class TreemapDataLabelProvider extends RectangleSeriesDataLabelProvider {
constructor(options?: IRectangleSeriesDataLabelProviderOptions) {
super(options);
}
// Override "getText" method to provide dynamic text based on rectangle size
getText(state: any): string {
const metadata = state.getMetaData() as any;
return (
`${formatNumber(metadata.accu, ENumericFormat.Engineering, 2)}$` +
(metadata.profit === 0
? ""
: `\n${metadata.profit > 0 ? "+" : ""}${formatNumber(
metadata.profit,
ENumericFormat.Engineering,
this.precision
)}$`)
);
}
}
// Prepare data and create rectangle series
const rectangleSeries = new FastRectangleRenderableSeries(wasmContext, {
dataSeries: new XyxyDataSeries(wasmContext, {
xValues: generatedData.map((d, i) => i),
yValues: generatedData.map((d) => d.prior),
x1Values: generatedData.map((d, i) => i),
y1Values: generatedData.map((d) => d.accu),
metadata: generatedData as any[],
}),
columnXMode: EColumnMode.Mid,
columnYMode: EColumnYMode.TopBottom,
dataPointWidthMode: EDataPointWidthMode.Range,
stroke: "black",
strokeThickness: 1,
opacity: 0.5,
fill: appTheme.DarkIndigo,
dataLabelProvider: new TreemapDataLabelProvider({
skipMode: EDataLabelSkipMode.ShowAll,
color: "white",
style: {
fontSize: 11,
multiLineAlignment: EMultiLineAlignment.Center,
lineSpacing: 5,
},
horizontalTextPosition: EHorizontalTextPosition.Center,
verticalTextPosition: EVerticalTextPosition.Center,
metaDataSelector: (md: unknown) => {
return (md as { profit: string }).profit;
},
}),
paletteProvider: new CustomFillProvider(),
});
sciChartSurface.renderableSeries.add(rectangleSeries);
// Add interactivity modifiers
sciChartSurface.chartModifiers.add(
new ZoomPanModifier({ enableZoom: true }),
new ZoomExtentsModifier(),
new MouseWheelZoomModifier()
);
return { sciChartSurface, wasmContext };
};
This React Charts example creates a Waterfall Chart using SciChart.js, demonstrating how to integrate the powerful charting library within a React application via the SciChart React component.
The chart logic is encapsulated in drawExample.ts, which initializes the SciChartSurface asynchronously. The React component simply passes this function to <SciChartReact initChart={drawExample}>. The implementation uses FastRectangleRenderableSeries with EColumnMode.Mid for centered columns and custom data labels.
The chart features dynamic coloring through a custom IFillPaletteProvider, multi-line data labels showing both cumulative and delta values, and responsive design through the ChartWrapper CSS class. Interactive modifiers enable zooming and panning.
The example shows proper React integration by separating chart logic from presentation. The async initialization pattern ensures optimal performance, while the contained component structure makes it easy to reuse. For more on React best practices, see Creating a SciChart React Component.








































