@ant-design/charts, @nivo/bar, @visx/shape, and recharts represent four distinct approaches to data visualization in React. recharts offers a declarative, component-based API built on D3 internals, prioritizing ease of use for standard charts. @nivo/bar provides a highly customizable, SVG-based bar chart component with rich animation and tooltip capabilities as part of a larger ecosystem. @visx/shape is a low-level collection of reusable React components for generating SVG paths (like arcs and lines) based on D3 scales, intended for building custom visualizations from scratch. @ant-design/charts wraps G2Plot to offer opinionated, enterprise-grade charts with sensible defaults and tight integration into the Ant Design system.
When selecting a charting library for a production React application, the decision often comes down to a tension between development speed, customization depth, and rendering performance. The four packages in question β @ant-design/charts, @nivo/bar, @visx/shape, and recharts β occupy different points on this spectrum. Let's break down how they handle data binding, rendering mechanisms, and customization.
The most critical architectural difference lies in how much of the visualization logic is hidden from you.
recharts and @ant-design/charts act as high-level wrappers. They abstract away the math and SVG details, letting you compose charts using declarative components.
// recharts: Declarative composition
<LineChart width={500} height={300} data={data}>
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="uv" stroke="#8884d8" />
</LineChart>
// @ant-design/charts: Config-driven approach
<Line data={data} xField="date" yField="value" />
@nivo/bar sits in the middle. It is a specialized component that handles the heavy lifting of scales and layout but exposes deep props for styling specific elements like bars or grids.
// @nivo/bar: Specialized component with deep props
<Bar
data={data}
keys={['hot-dog', 'burger', 'sandwich']}
indexBy="country"
margin={{ top: 50, right: 130, bottom: 50, left: 60 }}
barSpacing={0.5}
/>
@visx/shape is a low-level primitive. It does not know what a "chart" is; it only knows how to draw a shape based on data you provide. You must manually set up scales, axes, and tooltips.
// @visx/shape: Manual composition of primitives
<svg width={width} height={height}>
<Arc
data={pie(data)}
outerRadius={radius}
fill={d => color(d.data.key)}
/>
{/* You must manually add tooltips and axes here */}
</svg>
Performance under load depends heavily on whether the library uses SVG or Canvas.
recharts relies entirely on SVG. Every data point becomes a DOM node. This makes styling easy via CSS but causes significant lag when rendering thousands of points.
// recharts: Generates hundreds of <circle> or <path> DOM nodes
// Performance degrades noticeably with >1000 data points
<ScatterChart data={largeDataset}>
<Scatter dataKey="z" fill="#8884d8" />
</ScatterChart>
@nivo/bar also uses SVG but optimizes updates using React.memo and specific transition libraries. It handles moderate datasets well but still faces DOM limits.
// @nivo/bar: Optimized SVG rendering
// Uses internal memoization to prevent unnecessary re-renders of static parts
<Bar data={data} animate={true} motionStiffness={90} />
@ant-design/charts (powered by G2Plot) often defaults to Canvas for complex charts or large datasets, switching to SVG only when necessary for interactivity. This allows it to render tens of thousands of points smoothly.
// @ant-design/charts: Can utilize Canvas renderer internally
// Configured automatically based on chart type and data volume
<Scatter data={hugeDataset} renderer="canvas" />
@visx/shape is renderer-agnostic but typically used with SVG since it outputs React components. However, because you control the structure, you can swap the parent container for a Canvas implementation if you write the drawing logic yourself.
// @visx/shape: Typically SVG, but architecture allows Canvas swap
// You define the <svg> tag, so you could theoretically replace it with <canvas>
<svg>{shapes.map(d => <path key={d.id} d={d.path} />)}</svg>
How do you change the look and feel when the default isn't enough?
@ant-design/charts uses a configuration object. You pass a single prop with nested options. This is clean but can become unwieldy for unique designs.
// @ant-design/charts: Deep config object
<Bar
config={{
data,
xField: 'type',
yField: 'sales',
label: { position: 'middle', style: { fill: '#fff' } },
interactions: [{ type: 'element-active' }]
}}
/>
recharts uses child components. You customize by adding or modifying nested tags like <CustomizedShape />. This feels very "React-like" but can lead to deeply nested JSX.
// recharts: Child component customization
<BarChart data={data}>
<Bar dataKey="uv" fill="#8884d8">
<LabelList dataKey="uv" position="top" />
</Bar>
<CustomTooltip content={<MyCustomTooltip />} />
</BarChart>
@nivo/bar mixes both. It accepts props for common changes and render props (functions as children) for complex cell rendering.
// @nivo/bar: Render props for custom cells
<Bar
data={data}
label={({ id, value }) => `${id}: ${value}`}
renderBar={({ bar, x, y, width, height }) => (
<rect x={x} y={y} width={width} height={height} fill="url(#gradient)" />
)}
/>
@visx/shape offers full control. Since you are building the chart, you apply standard React patterns (CSS, inline styles, wrapper components) to every element.
// @visx/shape: Standard React styling
{shapes.map(shape => (
<path
key={shape.id}
d={shape.path}
className="my-custom-chart-bar" // Use CSS classes
style={{ transition: 'all 0.3s' }} // Inline styles
/>
))}
Your build size strategy differs significantly depending on the choice.
recharts and @ant-design/charts are generally imported as larger chunks. While tree-shaking helps, you often pull in logic for chart types you aren't using.
// recharts: Often imports the whole suite logic
import { LineChart, Line } from 'recharts';
// @ant-design/charts: Imports the G2Plot engine
import { Line } from '@ant-design/charts';
@nivo and @visx are designed for modular imports. You only pay for what you use.
// @nivo: Import only the bar chart logic
import { Bar } from '@nivo/bar';
// @visx: Import only the shape generator you need
import { arc } from '@visx/shape';
import { scaleLinear } from '@visx/scale';
Despite their differences, these libraries share common foundations.
All four rely on D3 principles for scales, axes, and data transformation, even if they hide the D3 API.
// All implicitly or explicitly use D3 scales
// recharts/nivo/visx/ant-design all map data domains to pixel ranges
const xScale = scaleLinear({ domain: [0, max], range: [0, width] });
Each provides a way to handle window resizing, though the implementation varies.
// recharts: ResponsiveContainer wrapper
<ResponsiveContainer width="100%" height={300}>
<LineChart>{/*...*/}</LineChart>
</ResponsiveContainer>
// @nivo: Built-in responsive prop
<BarResponsive data={data} />
// @visx: ParentSize higher-order component
<ParentSize>{({ width, height }) => <Chart width={width} height={height} />}</ParentSize>
All four offer strong TypeScript definitions, crucial for large-scale applications.
// All support typed data props
interface DataPoint { date: string; value: number; }
const data: DataPoint[] = [...];
// TS will catch mismatches in dataKey or config props across all libraries
| Feature | recharts | @nivo/bar | @visx/shape | @ant-design/charts |
|---|---|---|---|---|
| Primary Goal | Rapid Dev | Beautiful Defaults | Custom Systems | Enterprise Dashboards |
| Rendering | SVG | SVG | SVG (mostly) | Canvas/SVG Hybrid |
| API Style | Declarative Components | Config + Render Props | Low-level Primitives | Config Object |
| Learning Curve | Low | Medium | High | Low |
| Customization | Moderate | High | Unlimited | Moderate (Config limited) |
| Best For | Admin Panels, MVPs | Marketing Sites, Reports | Design Systems, Unique Viz | Complex Data Apps |
recharts is the pragmatic choice for standard business applications. If you need a line chart by tomorrow and your dataset is under 1,000 points, this is the safest bet. It trades performance for developer speed.
@nivo/bar is the aesthetic choice. When the chart is a central feature of the page and needs to look polished with minimal effort, Nivo's defaults are superior. It balances ease of use with visual fidelity.
@visx/shape is the architect's choice. Use this when you are building a design system or a product where the visualization is a core differentiator. It requires more code but removes all ceilings on what you can build.
@ant-design/charts is the enterprise choice. If you are already using Ant Design and need to display complex, large-scale data without hiring a data viz specialist, this wrapper provides the most power out of the box.
Final Thought: There is no single "best" library. The right tool depends on whether your bottleneck is development time (recharts, @ant-design/charts) or visual uniqueness/performance (@visx/shape, @nivo).
Choose @ant-design/charts if you are building an enterprise dashboard within the Ant Design ecosystem and need complex charts (like advanced analytics or maps) with minimal configuration. It is ideal when you prefer opinionated defaults and unified styling over granular control of every SVG element.
Choose @nivo/bar if you need a specific, high-quality bar chart with sophisticated animations, interactive tooltips, and responsive design out of the box. It fits well when you want better performance than full DOM-based solutions but don't need to build the entire visualization engine yourself.
Choose @visx/shape if you are architecting a custom design system or unique data visualization where standard chart types do not fit. It is the right choice when you need the mathematical power of D3 scales and shape generators but want to render them using native React components without the overhead of a full charting library.
Choose recharts if your team needs to rapidly deploy standard business charts (line, bar, pie) with a simple, declarative API that junior developers can grasp quickly. It is best suited for projects where development speed and community support outweigh the need for cutting-edge rendering performance or highly custom geometric shapes.
<img src=https://gw.alipayobjects.com/mdn/rms_19b204/afts/img/A*ixVAQrEoCTcAAAAAAAAAAAAAARQnAQ />
$ npm install @ant-design/charts
import React from 'react';
import { Line } from '@ant-design/charts';
const Page: React.FC = () => {
const data = [
{ year: '1991', value: 3 },
{ year: '1992', value: 4 },
{ year: '1993', value: 3.5 },
{ year: '1994', value: 5 },
{ year: '1995', value: 4.9 },
{ year: '1996', value: 6 },
{ year: '1997', value: 7 },
{ year: '1998', value: 9 },
{ year: '1999', value: 13 },
];
const config = {
data,
xField: 'year',
yField: 'value',
};
return <Line {...config} />;
};
export default Page;
Preview
Your contributions are always welcome! Please Do have a look at the issues first.
DingTalk group number: 44788198 .
MIT