Choosing the right chart library is crucial for modern data-driven applications. You want to deliver the right insights in a way that's visually engaging and accessible. You also want your developers to be able to navigate the platform effortlessly to support faster implementation. From real-time performance to responsive debugging support and security updates, all of this should feed into your decision when choosing the best React chart library for your use case.
An effective way to choose is to compare the market leaders and popular chart libraries.
Two popular solutions are React Charts (TanStack) and SciChart. This guide compares the features, performance and support that both React chart libraries offer, so you can choose the right one for your developers and data visualisation projects. We'll also share a direct visualisation comparison with our developer's insights into the differences in usability and performance.
Let's start with a look at the rendering engine, performance, chart types and suitable use cases for each library.
Feature Comparison of SciChart & TanStack
The table below compares features, performance, customisation capabilities and use cases.
| React Charts (TanStack) | SciChart | |
|---|---|---|
| Rendering Engine | D3 | In-house developed, GPU-accelerated |
| Performance | Handles between 1,000 and 10,000 data points with 60 fps interactions | Displays millions of data points in real time |
| Chart Types | Many variations of X/Y charts, including line, area, bar, column and bubble charts | Over 100 React chart examples covering variations of over 30 2D and 3D chart types |
| Customisation | Simple customisations such as tooltips, labels and annotations | The PaletteProvider API and CustomModifier give almost endless customisation and interactivity options |
| Use Cases | Simpler charts that don't need huge, complex datasets processed in real time | The most demanding data projects, from complex big data across multiple streams to real-time visualisation of millions of data points. Used by all the top F1 racing teams, 90% of the leading MedTech companies and over half of Fortune 500 companies in aerospace and defence |
TanStack's main selling point is the ability to create React charts with zero SVG knowledge required. They describe themselves as a solution for simple, immersive and interactive charts for React.
TanStack uses the D3 rendering engine to provide some styling customisations, whereas SciChart uses a custom-built, GPU-accelerated rendering engine for superior real-time performance.
Performance & Scalability
▶️ Watch the JavaScript Chart Library Performance Demo
For applications that require real-time updates (e.g. stock trading apps), performance is non-negotiable. SciChart delivers exceptional real-time performance for thousands, or even millions, of data points. In comparison, because TanStack is powered by D3, it comfortably handles far less data.
Using SVG charts with TanStack, you can expect to process around 1,000 data points. You can boost this to 10,000 data points with Canvas, but that still falls well short of what SciChart can process.
Security of Your Data
Security is another consideration. An open-source React chart library may not be updated as often as a paid one such as SciChart. This can leave open-source solutions vulnerable to bugs or incompatibility issues, and it reduces the competitiveness of what you're offering end users.
Many user questions go unanswered on open-source libraries, whereas SciChart offers a responsive support service. A ticket-based system means issues are better organised and prioritised, and they won't get lost. By SciChart's standards, responsive means you should receive a reply within one business day, or even faster. This service is available to paying customers and helps developers work through issues with big data visualisations while accelerating development.
Ease of Use & Developer Experience
TanStack offers a "try in 60 seconds" feature that lets developers quickly trial a code example to see how it runs.
SciChart has a little more to the installation process, as it isn't open-source. But there are straightforward step-by-step guides, APIs and examples. In fact, we offer hundreds of demos and documentation pages that make building the chart you need much easier, along with actively updated community forums.
Pricing & Licensing
TanStack is open-source, meaning it's free.
SciChart offers a free community version that includes all the functionality, features and performance for non-commercial use, though charts will carry a watermark. To get a taste of what SciChart offers and compare it with open-source chart libraries, it's worth trialling SciChart for free.
If there's an ROI consideration or a specific outcome to achieve, you may find SciChart is the only one of the two that can deliver.
Paid users can use SciChart in commercial applications, and all charts are free of watermarks.
The price of a SciChart licence depends on how many developers you need to supply and which platforms and additional support you'd like to include.
This might look like a significant upfront cost, but developer time is also worth a lot of money. That's where ROI comes in:
- Cost of developer time: $75/hour ($600 per day)
- Cost of a SciChart licence: approx. $1,699 for SciChart WPF 2D Professional
- Only 3 developer days need to be saved in a year before SciChart pays for itself
Use Case Recommendations
TanStack React Charts, being relatively simple, is well suited to use cases that don't demand high interactivity or complex data visualisation, such as basic dashboards and reporting tools.
SciChart's high-performance capabilities make it ideal for complex visualisations and large datasets. It's particularly suited to applications that need real-time updates or have demanding performance needs, such as financial applications, scientific data analysis and engineering simulations.
The choice ultimately depends on your project's requirements. Simpler applications may only need TanStack React Charts, while more complex ones with higher performance needs will benefit from SciChart.
Creating a Real-Time JavaScript Chart with SciChart.js and React Charts TanStack
To show the difference, we created a simple real-time performance demo using both SciChart.js and React Charts TanStack, and shared our verdict.
Setting Up a New React Project
First, we set up a new project. A recommended setup is React, Webpack and TypeScript.
package.json
{
"name": "scichart-reactcharts",
"version": "1.0.0",
"description": "scichart vs react charts tanstack",
"main": "src/index.tsx",
"scripts": {
"build": "webpack --mode production",
"start": "ts-node server",
"dev": "webpack-dev-server --mode development"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"react": "^19.0.0",
"react-charts": "^3.0.0-beta.57",
"react-dom": "^19.0.0",
"scichart": "^3.5.742",
"ts-node": "^10.9.2"
},
"devDependencies": {
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"copy-webpack-plugin": "^13.0.0",
"prettier": "^3.5.3",
"ts-loader": "^9.5.2",
"typescript": "^5.8.2",
"webpack": "^5.98.0",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.0"
}
}
webpack.config.js
const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
entry: "./src/index.tsx",
performance: {
hints: false,
},
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".js", ".ts", ".tsx"],
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "build"),
},
plugins: [
new CopyPlugin({
patterns: [
{ from: "src/index.html", to: "" },
{ from: "node_modules/scichart/_wasm/scichart2d.data", to: "" },
{ from: "node_modules/scichart/_wasm/scichart2d.wasm", to: "" },
{ from: "node_modules/scichart/_wasm/scichart3d.data", to: "" },
{ from: "node_modules/scichart/_wasm/scichart3d.wasm", to: "" },
],
}),
],
};
tsconfig.json
{
"compilerOptions": {
"outDir": "./build",
"sourceMap": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": false,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"module": "commonjs",
"target": "es6",
"jsx": "react-jsx",
"allowJs": true,
"typeRoots": ["./src/types", "./node_modules/@types"],
"esModuleInterop": true,
"skipLibCheck": false,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
index.html
<html lang="en-us">
<head>
<meta charset="utf-8" />
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>SciChart and React Charts Tanstack</title>
<script async type="text/javascript" src="bundle.js"></script>
</head>
<body></body>
</html>
index.tsx
import { createRoot } from "react-dom/client";
import App from "./App";
// Clear the existing HTML content
document.body.innerHTML = '<div id="app"></div>';
const root = createRoot(document.getElementById("app"));
root.render(<App />);
Create a Real-Time Demo with React Charts TanStack
Let's create a demo that draws three random walk line charts in real time. For random walk generation, we'll use this class:
RandomWalkGenerator.ts
export class RandomWalkGenerator {
private readonly bias: number;
private last: number;
private i: number;
private _seed: number;
constructor(bias: number = 0.01) {
this.bias = bias;
this.reset();
}
public Seed(seed: number) {
this._seed = seed % 2147483647;
if (this._seed <= 0) this._seed += 2147483646;
return this;
}
public reset() {
this.i = 0;
this.last = 0;
}
public getRandomWalkSeries(count: number): { xValues: number[]; yValues: number[] } {
const xValues: number[] = [];
const yValues: number[] = [];
const random = () =>
this._seed === undefined ? Math.random() : (this.nextSeeded() - 1) / 2147483646;
for (let i = 0; i < count; i++) {
const next: number = this.last + (random() - 0.5 + this.bias);
xValues.push(this.i++);
yValues.push(next);
this.last = next;
}
return { xValues, yValues };
}
private nextSeeded() {
return (this._seed = (this._seed * 16807) % 2147483647);
}
}
We'll append 100 data points to each data series every 20 milliseconds, which makes the charts render at 50 frames per second (FPS). We'll keep appending until we reach 10,000 points, and measure how long it takes to render them all.
This is the ReactChartsDemo component we'll use to render the real-time demo with the React Charts TanStack JavaScript library. The React Charts TanStack API is very intuitive and supports TypeScript, which makes the demo easy to build.
ReactChartsDemo.tsx
import { useMemo, useState, useEffect, useRef } from "react";
import { AxisOptions, Chart } from "react-charts";
import { RandomWalkGenerator } from "../RandomWalkGenerator";
type TDataPoint = {
x: number;
y: number;
};
type TSeries = {
label: string;
data: TDataPoint[];
};
const initialDataSeries: TSeries[] = [
{ label: "Series 1", data: [] },
{ label: "Series 2", data: [] },
{ label: "Series 3", data: [] },
];
const numberOfPointsPerTimerTick = 100; // 100 points are appended every timer tick
const timerInterval = 20; // the timer ticks every 20 milliseconds (equivalent to 50 FPS)
const maxPoints = 10000; // max points for a single series before the demo stops
let perfStart: number;
let perfEnd: number;
export default function ReactChartsDemo() {
const timerIdRef = useRef<NodeJS.Timeout>(null);
const [dataSeries, setDataSeries] = useState(initialDataSeries);
const [timeElapsed, setTimeElapsed] = useState("");
const primaryAxis = useMemo(
(): AxisOptions<TDataPoint> => ({
getValue: (datum) => datum.x,
scaleType: "linear",
}),
[]
);
const secondaryAxes = useMemo(
(): AxisOptions<TDataPoint>[] => [
{
getValue: (datum) => datum.y,
scaleType: "linear",
},
],
[]
);
useEffect(() => {
const randomWalkGenerators = [1, 2, 3].map((_) => {
return new RandomWalkGenerator(0);
});
const stopUpdate = () => {
clearTimeout(timerIdRef.current);
timerIdRef.current = undefined;
randomWalkGenerators.forEach((rw) => rw.reset());
};
const updateFunc = () => {
if (initialDataSeries[0].data.length >= maxPoints) {
perfEnd = performance.now();
setTimeElapsed(`${((perfEnd - perfStart) / 1000).toFixed(2)} seconds`);
stopUpdate();
return;
}
randomWalkGenerators.forEach((randomWalk, index) => {
// Get the next N random walk x,y values
const { xValues, yValues } = randomWalk.getRandomWalkSeries(numberOfPointsPerTimerTick);
for (let i = 0; i < numberOfPointsPerTimerTick; i++) {
const x = xValues[i];
const y = yValues[i];
initialDataSeries[index].data.push({ x, y });
}
});
setDataSeries([...dataSeries]);
timerIdRef.current = setTimeout(updateFunc, timerInterval);
};
const startUpdate = () => {
if (timerIdRef.current) {
stopUpdate();
}
perfStart = performance.now();
timerIdRef.current = setTimeout(updateFunc, timerInterval);
};
startUpdate();
}, []);
return (
<div>
<div style={{ width: 800, height: 533 }}>
<Chart
options={{
data: dataSeries,
primaryAxis,
secondaryAxes,
initialWidth: 800,
initialHeight: 533.33,
}}
/>
</div>
<p>Time elapsed: {timeElapsed}</p>
</div>
);
}
The result is a chart that works fine for the first 1,000 points. After that it slows down, taking 53 seconds to render the full 10,000-point dataset, when the expected time at 50 FPS with 100 points per tick is 2 seconds.

React Charts TanStack 10,000 Points Demo
Create the Real-Time Demo with SciChart.js
Now we'll create the same chart using the hardware-accelerated SciChart.js data visualisation library, with the same random walk generator class.
We'll create two files: createChart.ts with the charting code, and SciChartDemo.tsx with the React code.
createChart.ts
import {
EAutoRange,
EAxisAlignment,
EColor,
EDragMode,
FastLineRenderableSeries,
MouseWheelZoomModifier,
NumericAxis,
RubberBandXyZoomModifier,
SciChartSurface,
XAxisDragModifier,
XyDataSeries,
YAxisDragModifier,
ZoomExtentsModifier,
} from "scichart";
import { RandomWalkGenerator } from "../RandomWalkGenerator";
const numberOfPointsPerTimerTick = 100; // 100 points are appended every timer tick
const timerInterval = 20; // the timer ticks every 20 milliseconds (equivalent to 50 FPS)
const maxPoints = 10000; // max points for a single series before the demo stops
let perfStart: number;
let perfEnd: number;
export const createChart = async (rootElement: string | HTMLDivElement) => {
const { wasmContext, sciChartSurface } = await SciChartSurface.createSingle(rootElement);
// Create an XAxis and YAxis with autoRange=Always
const xAxis = new NumericAxis(wasmContext, { autoRange: EAutoRange.Always });
sciChartSurface.xAxes.add(xAxis);
const yAxis = new NumericAxis(wasmContext, {
autoRange: EAutoRange.Always,
axisAlignment: EAxisAlignment.Left,
});
sciChartSurface.yAxes.add(yAxis);
// Create some DataSeries
const dataSeries: XyDataSeries[] = [
new XyDataSeries(wasmContext, { containsNaN: false, isSorted: true }),
new XyDataSeries(wasmContext, { containsNaN: false, isSorted: true }),
new XyDataSeries(wasmContext, { containsNaN: false, isSorted: true }),
];
const seriesColors = [EColor.Blue, EColor.Orange, EColor.Purple];
// Create some FastLineRenderableSeries bound to each dataSeries and add to the chart
dataSeries.map((ds, index) => {
sciChartSurface.renderableSeries.add(
new FastLineRenderableSeries(wasmContext, {
dataSeries: ds,
strokeThickness: 2,
stroke: seriesColors[index],
})
);
});
// Add some interactivity modifiers. These are only operational when the demo is paused
// as interactivity conflicts with AutoRange.Always
sciChartSurface.chartModifiers.add(
new RubberBandXyZoomModifier(),
new MouseWheelZoomModifier(),
new XAxisDragModifier({ dragMode: EDragMode.Panning }),
new YAxisDragModifier({ dragMode: EDragMode.Panning }),
new ZoomExtentsModifier()
);
// This class generates some data for our example
// It generates a random walk, which is a line which increases or decreases by a random value
// each data-point
const randomWalkGenerators = [1, 2, 3].map((_) => {
return new RandomWalkGenerator(0);
});
let timerId: NodeJS.Timeout;
// Function called when the user clicks stopUpdate button
const stopUpdate = () => {
clearTimeout(timerId);
timerId = undefined;
randomWalkGenerators.forEach((rw) => rw.reset());
// Disable autoranging on X when the demo is paused. This allows zooming and panning
xAxis.autoRange = EAutoRange.Once;
};
const updateFunc = (setTimeElapsedFn: (v: string) => void) => {
if (dataSeries[0].count() >= maxPoints) {
perfEnd = performance.now();
setTimeElapsedFn(`${((perfEnd - perfStart) / 1000).toFixed(2)} seconds`);
stopUpdate();
return;
}
randomWalkGenerators.forEach((randomWalk, index) => {
// Get the next N random walk x,y values
const { xValues, yValues } = randomWalk.getRandomWalkSeries(numberOfPointsPerTimerTick);
// Append these to the dataSeries. This will cause the chart to redraw
dataSeries[index].appendRange(xValues, yValues);
});
timerId = setTimeout(() => updateFunc(setTimeElapsedFn), timerInterval);
};
// Function called when the user clicks startUpdate button
const startUpdate = (setTimeElapsedFn: (v: string) => void) => {
if (timerId) {
stopUpdate();
}
// Enable autoranging on X when running the demo
xAxis.autoRange = EAutoRange.Always;
dataSeries.forEach((ds) => ds.clear());
perfStart = performance.now();
timerId = setTimeout(() => updateFunc(setTimeElapsedFn), timerInterval);
};
let lastRendered = Date.now();
sciChartSurface.rendered.subscribe(() => {
const currentTime = Date.now();
const timeDiffSeconds = new Date(currentTime - lastRendered).getTime() / 1000;
lastRendered = currentTime;
const fps = 1 / timeDiffSeconds;
const renderStats = {
numberPoints:
sciChartSurface.renderableSeries.size() *
sciChartSurface.renderableSeries.get(0).dataSeries.count(),
fps,
};
});
return { wasmContext, sciChartSurface, controls: { startUpdate, stopUpdate } };
};
SciChartDemo.tsx
import { useEffect, useState, useRef } from "react";
import { SciChartSurface } from "scichart";
import { createChart } from "./createChart";
export default function SciChartDemo() {
const chartId = "chartId";
const chartRef = useRef<SciChartSurface>(undefined);
const [timeElapsed, setTimeElapsed] = useState("");
useEffect(() => {
(async () => {
const { sciChartSurface, controls } = await createChart(chartId);
chartRef.current = sciChartSurface;
controls.startUpdate(setTimeElapsed);
})();
return () => chartRef.current?.delete();
}, []);
return (
<div>
<div id={chartId} />
<p>Time elapsed: {timeElapsed}</p>
</div>
);
}
As you can see, the SciChart.js example contains more code than the React Charts TanStack one. But on top of the basic functionality, we added chart modifiers so the chart is interactive once the demo finishes, letting you zoom in, zoom out and pan:
sciChartSurface.chartModifiers.add(
new RubberBandXyZoomModifier(),
new MouseWheelZoomModifier(),
new XAxisDragModifier({ dragMode: EDragMode.Panning }),
new YAxisDragModifier({ dragMode: EDragMode.Panning }),
new ZoomExtentsModifier()
);
We also made some performance optimisations. Setting containsNaN: false and isSorted: true tells the engine it can use binary search and skip null checks:
const dataSeries: XyDataSeries[] = [
new XyDataSeries(wasmContext, { containsNaN: false, isSorted: true }),
new XyDataSeries(wasmContext, { containsNaN: false, isSorted: true }),
new XyDataSeries(wasmContext, { containsNaN: false, isSorted: true }),
];
The result is a real-time chart that renders at almost 50 FPS. To be precise, we got 100 frames / 2.28 seconds = 43.85 FPS.
SciChart.js vs React Charts TanStack: The Final Verdict
We created a real-time performance demo with both React Charts TanStack and SciChart.js. React Charts TanStack achieved about 2 FPS, while SciChart.js reached more than 40 FPS. That's no surprise, given React Charts TanStack uses SVG and SciChart.js uses a WebGL canvas.
React Charts TanStack has a nice API and is a good solution if you don't need real-time charts or to visualise large numbers of data points. SciChart.js is a little more verbose, but it allows fine-tuning and stands out for performance.
When working with large numbers of data points or needing real-time performance, the right choice is a hardware-accelerated charting library like SciChart.js.
Developer note: Don't run both examples on the same page at once. Because JavaScript is single-threaded, you'll get the worst of both results.
👉 View the source code on GitHub
Which React Chart Library Is Best for You?
For simpler applications, React Charts are suitable. For big data handling, demanding performance requirements, pioneering insights and enhanced customisation, SciChart is the one you want.
Remember, SciChart provides a free community version that gives you all the performance and visual customisation of the paid version. It's a great way to see what SciChart offers and compare it with open-source chart libraries.
Find out how we can help your development team build exceptional, high-performance React data visualisations from scratch. Our hardware-accelerated graphics engine renders tens of millions of data points with real-time updates, with plenty of customisation options to create exactly the visualisations your React charting project needs.


Top comments (0)