
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 effortlessly navigate the platform, to support faster implementation. From ensuring that you have the real-time performance you require to getting responsive debugging support and security updates, all this and more should support your decision to find 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 include React Charts (TanStack) and SciChart. This guide compares the features, performance and support that both React chart libraries have to offer so you can choose the right one for your developers and data visualization projects. We’ll also be sharing a direct visualization comparison with our developer’s insights into the differences in usability and performance.
Let’s start our comparison with a look at the rendering engine, performance, chart types that the two React chart libraries have to offer and the suitable use cases.
Feature Comparison of SciChart & TanStack
The below table compares features, performance, customization capabilities, pricing, and more.
| 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. | Display millions of data points in real-time. |
| Chart Types | React Charts can render many variations of X/Y charts, including, but not limited to, line, area, bar, column and bubble charts. | SciChart offers over 100 React chart examples for variations of over 30 2D and 3D React charts. |
| Customization | Simple customisations can be achieved, such as adding tooltips, labels and annotations. | SciChart offers a PaletteProvider API and CustomModifier which give you almost endless customization and interactivity options for the presentation of your charts. |
| Use Cases | TanStack is often better suited for simpler chart creations that don’t require huge, complex data sets to be processed in real-time. | Organizations that require the most demanding data projects—whether it’s processing complex, big data from multiple data streams or needing to handle real-time visualizations for millions of data points, SciChart reliably handles this. It’s why we’re the chart library of choice for:
|
TanStack’s main selling point is the ability to create React charts with zero SVG knowledge required. They describe themselves as the solutions for ‘Simple, immersive & interactive charts for React’.
TanStack uses the D3 rendering engine to provide some styling customizations, whereas SciChart uses a custom-built GPU-accelerated rendering engine to inject your data with superior real-time performance.
Performance & Scalability
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, as TanStack is powered by D3, it can comfortably handle a lot 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 this still doesn’t live up to the amount of data 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 leaves open-source solutions vulnerable to bugs or incompatibility issues. It also reduces the competitiveness of what you’re offering for end users.
Many user questions go unanswered on open-source libraries, whereas SciChart offers a responsive support service. Using a ticket-based system means that issues can be better organized and prioritized—they also won’t get lost! By SciChart’s standards, responsive means that you should receive a response within one business day, or even faster! This service is available to paying customers but massively helps developers sift through issues they may encounter with big data visualizations while also helping to accelerate 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 we’re not open-source. But we have straightforward step-by-step guides, APIs and examples. In fact, they offer hundreds of demos and documentation that make building the chart you need much easier, with community forums actively updated as well.
Pricing & Licensing
TanStack is open-source, meaning it’s free.
SciChart still offers a free community version that includes all the functionality, features and performance for non-commercial use. The data visualizations will also have watermarks. However, to get a taster of what SciChart has to offer and compare the significant difference from open-source chart libraries, it’s worth trialling SciChart for free.
If there’s an ROI consideration or a desired outcome to achieve, you may find that out of the two solutions, SciChart is the only one that can deliver.
For paid users, you’ll be able to use SciChart for commercial applications, and all your charts will be free of watermarks.
The price of a licence with SciChart will depend on how many developers you need to supply and which platforms and additional support you’d like to include.
This might look like a pricey upfront cost; however, developer time is also worth a lot of money. That’s where ROI is a notable consideration.
- Cost of developer time: $75 / hour ($600 per day)
- Cost of SciChart License: approx. ~$1699 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 simpler, is well-suited for use cases that don’t demand high interactivity or have complex data visualization requirements. These include applications such as basic dashboards and reporting tools.
On the other hand, SciChart’s high-performance capabilities make it ideal for handling complex visualizations and large datasets. It is particularly suited for applications that need real-time data updates or have demanding performance needs, such as financial applications, scientific data analysis, and engineering simulations.
The choice ultimately depends on the specific requirements of your data project; simpler applications may only need TanStack React Charts, while more complex ones with higher performance needs would benefit from SciChart’s capabilities.
Creating Real-Time JavaScript Chart with SciChart.js and React Charts TanStack
To help you visualize the difference, we are creating a simple real-time performance demo using SciChart.js and React Charts TanStack, and offering our verdict.
Setting Up a New React Project
First, we set up a new project. A recommended way of setting up a new project is to use 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 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 will use this class:
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 will append 100 data points to each data series every 20 milliseconds, which makes charts render at 50 frames per second (FPS). We will attach the data points until we get 10,000. In addition, we will measure the time it takes to render all 10,000 data points.
This is the ReactChartDemo component we will use to render the real-time demo with React Charts TanStack JavaScript library. The React Charts TanStack API is very intuitive and supports TypeScript, which makes it easy to create the demo.
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>
);
}As a result, we get this chart, which works fine for the first 1000 points. However, after 1000 points it gets slow and takes 53 seconds to render the whole data set of 10,000 points. Where the expected time when rendering at 50 FPS and appending 100 points at a time is 2 seconds.

Create the Real-Time Demo with SciChart.js
Now we will create the same chart using the SciChart.js data visualization library with hardware acceleration. We will use the same random walk generator class.
We will create two files createChart.ts with the code related to charting and index.ts with the React code.
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 } };
};
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, SciChart.js example contains more code than the React Charts TanStack example. But, besides the basic functionality, we have added chart modifiers to make the chart interactive after the demo finishes, so that we can zoom in/out and pan the chart.
sciChartSurface.chartModifiers.add(
new RubberBandXyZoomModifier(),
new MouseWheelZoomModifier(),
new XAxisDragModifier({ dragMode: EDragMode.Panning }),
new YAxisDragModifier({ dragMode: EDragMode.Panning }),
new ZoomExtentsModifier(),
);
and we did some performance optimizations containsNaN: false and isSorted: true tells the engine that it can use binary search and skip the 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 }),
];
As a result, we get the real-time chart that renders almost at 50 FPS. To be precise, we got 100 frames / 2.28 seconds = 43.85 FPS.

SciChart.js vs React Charts TanStack: What Was the Final Verdict?
We created a real-time performance demo using React Charts TanStack and SciChart.js. For React Charts TanStack, we achieved about 2 FPS. However, for SciChart.js we got more than 40 FPS. It was not a surprise, bearing in mind React Charts TanStack uses SVG and SciChart.js uses WebGL canvas.
React Charts TanStack has a nice API and it is a good solution if there is no need to do real-time charts or to visualize a large amount of data points. On the other hand, SciChart.js is a little bit more verbose in terms of the API, but it allows fine-tuning and stands out with its performance.
When working with a large number of data points or looking for a real-time performance, the right choice would be to use a hardware-accelerated charting library like SciChart.js.
Developer Note: When running the examples, refrain from running them simultaneously on one page, because of the JS single-threaded nature, you will get the worst of two results for both.
Which React Chart Library Is Best for You?
For simpler applications, React Charts are suitable. However, for big data handling, demanding performance requirements, pioneering insight creation and enhanced customizations, SciChart is the one you want.
Remember, SciChart provides a free community version that lets you tap into all the performance and visual customizations that you get with a paid version. It’s a fantastic way to get a taster of what SciChart has to offer and compare the significant enhancements compared to open-source chart libraries.
Find out more about how we could help you and your development team build exceptional, high-performance React data visualizations from scratch. Our hardware-accelerated graphics engine renders tens of millions of data points with real-time updates. You’ll also benefit from plenty of customization possibilities, helping you create the exact data visualizations required for your React charting project.
Recent Blogs

