Get in Touch
React Charting Library for Large Datasets: How to Handle Millions of Points Without Crashing - Red Surge Technology Blog
ReactData VisualizationPerformanceLarge DatasetsJavaScript

Most React charting tutorials show you 20 data points. The demo chart renders instantly, the tooltip hovers smoothly, and everything looks great. Then you try it with your real dataset—500,000 rows from a sensor, a year of stock ticks, or a million user events—and the browser freezes so hard you can hear the CPU fan spinning.

The truth is, not every charting library can handle large datasets. SVG-based libraries, which create a DOM node for every data point, start choking around a few thousand elements. Canvas-based libraries do much better, but even they need help when the numbers get really big. The solution isn't just picking the right library; it's combining the right library with the right rendering strategies.

In this guide, I'll show you which React charting libraries are actually built for large datasets, and the techniques—downsampling, canvas rendering, virtualization, and smart memoization—that keep charts smooth when your data could fill a spreadsheet from here to the moon.

Why SVG falls apart at scale

Most React chart libraries—Recharts, Victory, Nivo—render to SVG. SVG is great for a few reasons: you can style it with CSS, it's accessible, and each element is a separate DOM node that can have its own event handlers. But that last point is a killer for large datasets. Each data point becomes a circle, a line segment, or a rectangle in the DOM. 10,000 points means 10,000 DOM nodes. 100,000 points means 100,000 nodes. The browser has to layout, paint, and manage hit-testing for all of them. Scrolling or hovering becomes a slideshow.

Canvas rendering, on the other hand, draws everything to a single bitmap. There's only one DOM element—the canvas—so the browser doesn't care how many points you draw. Chart.js (via react-chartjs-2) uses canvas by default, and it can handle hundreds of thousands of points without breaking a sweat. The tradeoff is that you lose fine-grained CSS styling and per-point accessibility, but for large datasets, that's usually an acceptable compromise.

If you're still weighing your options, our React chart library performance benchmarks showed that canvas solutions are 10x faster than SVG at 10,000 points. The gap only widens from there.

The library shortlist for big data

Here's what I actually recommend when the data volume is the primary constraint:

  • react-chartjs-2 (Chart.js) – The best all-around canvas option. Handles huge datasets out of the box, has extensive options for performance (decimation, disabling animation, etc.), and is widely used. If you're unsure, start here.

  • visx with canvas – visx gives you low-level control. You can build a canvas-based scatter plot or line chart yourself, optimized exactly for your data shape. The bundle is tiny, and you only render what you need. Requires D3 knowledge, but for extreme performance, it's unbeatable.

  • Plotly.js with react-plotly.js – If you need WebGL rendering (for millions of points), Plotly is the heavyweight. It can push rendering to the GPU, making even 1M+ points interactive. The bundle is massive, though, so it's overkill for most dashboards.

  • Recharts (with heavy optimization) – Can work if you downsample aggressively and disable animations, but honestly, it's fighting against SVG's fundamental limits. I wouldn't choose it for a greenfield large-data project.

If your app is a typical dashboard with a few thousand points per chart, SVG is fine. But if you know you'll cross the 10k mark, skip straight to canvas.

Downsampling: do you really need to show every point?

The cheapest way to handle a million points is to not render a million points. Downsampling reduces the data to a manageable size before it ever hits the chart. There are several algorithms, but the simplest and most effective for time-series data is min-max decimation or largest triangle three buckets (LTTB).

For most visualizations, you can't perceive more than a few thousand points per pixel width anyway. If your chart is 800 pixels wide, showing more than 800–1600 points per series is pure waste. Downsample to fit the viewport, and the chart looks identical—but renders 100x faster.

Here's a basic min-max decimation for time series:

function downsample(data, maxPoints) {
  if (data.length <= maxPoints) return data;

  const bucketSize = Math.floor(data.length / maxPoints);
  const result = [];

  for (let i = 0; i < data.length; i += bucketSize) {
    const bucket = data.slice(i, i + bucketSize);
    const min = bucket.reduce((a, b) => (a.value < b.value ? a : b));
    const max = bucket.reduce((a, b) => (a.value > b.value ? a : b));
    // Keep both min and max to preserve spikes
    result.push(min, max);
  }

  return result;
}

This preserves the visual envelope of the data, so spikes and dips aren't lost. For line charts, you might also consider the LTTB algorithm, which does a better job of keeping the visual shape. Libraries like downsample or chartjs-plugin-downsample implement these for you.

If you're using Chart.js, the built-in decimation plugin is on by default for line charts. It automatically reduces the number of points drawn based on the canvas width. You can tune it with the decimation option:

const options = {
  decimation: {
    enabled: true,
    algorithm: "lttb",
    samples: 500,
  },
};

This single option can take a 1M-point dataset and render it as 500 representative points, making the chart interactive again.

Virtualization for bar charts and lists

Downsampling works for continuous series, but what about bar charts with thousands of bars? The answer is virtualization: only render the bars that are actually visible in the viewport, and draw the rest as a scrollable canvas or as placeholder space.

Canvas libraries like Chart.js handle this naturally because they redraw the visible area efficiently. But if you're using an SVG library, you'll need to implement your own virtualization—either by rendering only a slice of bars and updating on scroll, or by using a canvas overlay for the bars and SVG only for axes.

A simpler approach for bar charts with many categories: aggregate the bars into larger buckets. If you have 10,000 transactions per day for a year, show monthly totals instead of daily. The user can drill down if they need details. This is often the right product decision anyway—too many bars are visually overwhelming.

Memoization and preventing unnecessary re-renders

Even with canvas, your React component can still become a bottleneck if it re-renders on every data update. If the chart receives new props every second, and the whole component tree re-renders, you'll see jank even if the canvas drawing is fast.

The solution is to isolate the chart component and wrap it in React.memo, passing only stable, memoized props. Use useMemo to keep the data reference stable when the underlying data hasn't changed, and useCallback for event handlers. These patterns are covered in our guide on preventing unnecessary re-renders in React, but they're especially crucial for data-heavy charts.

For real-time updates, throttle or debounce the data stream before it reaches the chart. If updates arrive every 50ms, but the user can't perceive changes faster than 100ms, throttle to 100ms and cut the render frequency in half. Combine this with debounce and throttle patterns to smooth out bursts.

import { useMemo, memo } from "react";
import throttle from "lodash.throttle";

const BigChart = memo(({ data }) => {
  // Chart rendering uses memoized data
  return <CanvasChart data={data} />;
});

function ChartContainer({ rawData }) {
  const throttledData = useMemo(() => throttle(rawData, 100), [rawData]);

  return <BigChart data={throttledData} />;
}

WebGL: when you truly have millions of points

If you're plotting 1 million+ points and need interactivity like zooming and hovering, canvas alone might not cut it. This is where WebGL rendering comes in. WebGL uses the GPU to draw points, which is orders of magnitude faster than CPU-side canvas or SVG.

Plotly.js is the most popular library with WebGL support for scatter plots (via scattergl). It can handle 10 million points with smooth zoom and pan. The downside is a huge bundle (several hundred KB) and a different API from the React-centric libraries.

For most web dashboards, WebGL is overkill. But for scientific visualization, financial analysis, or any app that deals with truly massive point clouds, it's the only sane option.

A real-world story: rendering 1M points in a browser

A few years back, I worked on a genomics visualization tool that needed to display millions of data points along a chromosome. The initial version was built with D3 and SVG—every point was a tiny <circle>. It worked with 100,000 points, barely. With 1 million, the page locked up for 30 seconds and then crashed.

We rebuilt it using canvas and a custom scatter plot with downsampling. At 1 million points, we sampled down to about 50,000 visible points per screen, which looked identical to the full dataset at any zoom level. Then we added WebGL for the highest zoom levels, where individual points became visible and needed to be interactive. The result was a tool that could smoothly zoom from the whole genome down to a single base pair—something SVG could never handle.

The lesson: don't fight the rendering model. If your data is huge, use canvas or WebGL, downsample aggressively, and only render what the user can actually see.

Additional optimizations worth knowing

  • Disable animations for large datasets. Animation implies re-rendering every frame, which is expensive. For static or infrequently updated charts, turn it off.
  • Use data decimation for line charts (as mentioned) to reduce the number of drawn segments.
  • Limit point radius for scatter plots. A 1px radius renders much faster than a 5px radius with shadows.
  • Consider using requestAnimationFrame to batch chart updates and avoid layout thrashing.
  • Avoid heavy tooltips on hover if performance is critical. Tooltips that compute and render complex HTML can slow down even canvas charts. Use simple text tooltips.

Wrapping up

Large datasets don't have to mean a frozen UI. The key is to match the rendering technology to the data volume: SVG for small, canvas for large, WebGL for massive. Then apply downsampling, virtualization, and memoization to keep your React components as lightweight as possible.

If you're just starting, pick react-chartjs-2—it's the most straightforward path to canvas rendering and has built-in decimation. If you have D3 experience and need fine-grained control, build a custom canvas chart with visx. And if you're already using an SVG library like Recharts and can't switch, at least downsample your data to under 1,000 points per chart and disable animations.

For more on how these libraries compare in feature sets, see our React chart library comparison. And for raw performance numbers, our React chart library benchmarks have you covered.

The browser can handle more than you think—if you feed it right.


Struggling with large datasets in your React app? Red Surge Technology builds high-performance data visualizations that stay smooth at any scale. Get in touch to discuss your project.