Next.jsで使えるチャートライブラリを調べてみた
はじめに
こんにちは。
先日、Next.jsを使用した開発でチャートを実装する機会がありました。
チャートライブラリを選定することになったのですが、私はこれまでチャートの実装はほとんど経験がありません。(大昔にたしかVue.jsとChart.jsで実装をしたことはあるのですが、昔すぎて忘れました)
そこで、そもそもどんなライブラリが存在して、それぞれの特徴やメリット・デメリット、どういった状況でどのライブラリを選ぶべきかを調べることにしました。
本記事では、その調査結果を共有します。
今回比較するライブラリ
世の中には数多のライブラリがあります。
その全てを調べるのは結構大変なので、有名どころや比較対象にすると良さそうなライブラリを調べてピックアップしました。
エントリーしたライブラリ一覧
- Recharts
- D3.js
- Chart.js (react-chartjs-2)
- Nivo
- Apache ECharts
比較一覧表
| ライブラリ | 最新バージョン | GitHubスター | レンダリング | 学習コスト | カスタマイズ性 |
|---|---|---|---|---|---|
| Recharts | 3.7.0 | 26.6K | SVG | ◎ 低い | ○ 中程度 |
| D3.js | 7.9.0 | 111.9K | SVG/Canvas | △ 高い | ◎ 非常に高い |
| Chart.js (react-chartjs-2) | 5.3.1 | 6.9K | Canvas | ◎ 低い | ○ 中程度 |
| Nivo | 0.99.0 | 13.9K | SVG/Canvas/HTML | ○ 中程度 | ◎ 高い |
| Apache ECharts | 6.0.0 | 65.6K | Canvas/SVG | ○ 中程度 | ◎ 高い |
各ライブラリの詳細
1. Recharts
概要
RechartsはReact向けに設計されたチャートライブラリで、様々な種類のチャートを描画することができます。
複雑な描画ロジックを自分で書く必要がなく、データをコンポーネントのpropsに渡すだけで、自動的にSVGベースのグラフが描画されます。
公式サイト: https://recharts.github.io/
特徴
- Reactコンポーネントとして直感的に記述可能
- SVGベースでCSSによるスタイリングが容易
- ResponsiveContainerで簡単にレスポンシブ対応可能
- TypeScriptに対応
コード例
'use client';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
const data = [
{ name: '1月', uv: 4000, pv: 2400 },
{ name: '2月', uv: 3000, pv: 1398 },
{ name: '3月', uv: 2000, pv: 9800 },
];
export default function SalesChart() {
return (
<ResponsiveContainer width="100%" height={400}>
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="pv" stroke="#8884d8" />
<Line type="monotone" dataKey="uv" stroke="#82ca9d" />
</LineChart>
</ResponsiveContainer>
);
}
メリット
- 学習コストが低い(React経験者ならすぐに使える)
- コンポーネントベースで使いやすい
デメリット
- SVGにしか対応していない(大量のデータには不向き)
- 高度なカスタマイズには限界がある
- アニメーションの細かい制御が難しい
向いている案件
- 複雑な機能を必要としない場面
- 開発スピード重視の場面
- 他に使い慣れたチャートライブラリがない場面
向いていない案件
- 大規模なデータ処理
- 地図などの特殊なケース
- 細かなデザイン調整が必要な場合
2. D3.js
概要
D3.js(Data-Driven Documents)も定番です。
学習コストや実装コストが高い分、非常に自由度の高いチャート実装が可能なライブラリです。
公式サイト: https://d3js.org/
特徴
- 自由度が非常に高く、あらゆるチャートが作れる
- 機能ごとに分割されたパッケージ構成で、必要な部分だけインポート可能
- SVG、Canvas、HTMLすべてに対応
React(Next.js)との統合パターン
D3.jsをReact(Next.js)で使う場合、主に2つのアプローチがあります。
パターン1: D3をデータ処理にのみ使用
'use client';
import * as d3 from 'd3';
interface LinePlotProps {
data: number[];
width?: number;
height?: number;
marginTop?: number;
marginRight?: number;
marginBottom?: number;
marginLeft?: number;
}
export default function LinePlot({
data,
width = 640,
height = 400,
marginTop = 20,
marginRight = 20,
marginBottom = 20,
marginLeft = 20
}: LinePlotProps) {
const x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]);
const y = d3.scaleLinear(d3.extent(data) as [number, number], [height - marginBottom, marginTop]);
const line = d3.line((d, i) => x(i), y);
return (
<svg width={width} height={height}>
<path fill="none" stroke="currentColor" strokeWidth="1.5" d={line(data) || ''} />
<g fill="white" stroke="currentColor" strokeWidth="1.5">
{data.map((d, i) => (<circle key={i} cx={x(i)} cy={y(d)} r="2.5" />))}
</g>
</svg>
);
}
パターン2: D3でDOMを直接操作する(useRef + useEffect)
'use client';
import * as d3 from 'd3';
import { useRef, useEffect } from 'react';
interface LinePlotProps {
data: number[];
width?: number;
height?: number;
marginTop?: number;
marginRight?: number;
marginBottom?: number;
marginLeft?: number;
}
export default function LinePlot({
data,
width = 640,
height = 400,
marginTop = 20,
marginRight = 20,
marginBottom = 30,
marginLeft = 40
}: LinePlotProps) {
const gx = useRef<SVGGElement>(null);
const gy = useRef<SVGGElement>(null);
const x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]);
const y = d3.scaleLinear(d3.extent(data) as [number, number], [height - marginBottom, marginTop]);
const line = d3.line((d, i) => x(i), y);
useEffect(() => {
if (gx.current) d3.select(gx.current).call(d3.axisBottom(x));
}, [gx, x]);
useEffect(() => {
if (gy.current) d3.select(gy.current).call(d3.axisLeft(y));
}, [gy, y]);
return (
<svg width={width} height={height}>
<g ref={gx} transform={`translate(0,${height - marginBottom})`} />
<g ref={gy} transform={`translate(${marginLeft},0)`} />
<path fill="none" stroke="currentColor" strokeWidth="1.5" d={line(data) || ''} />
<g fill="white" stroke="currentColor" strokeWidth="1.5">
{data.map((d, i) => (<circle key={i} cx={x(i)} cy={y(d)} r="2.5" />))}
</g>
</svg>
);
}
メリット
- 細かいアニメーション制御など、自由な表現が可能
- 業界標準で情報が豊富
- 他のライブラリの基盤として使われている
- React、Next.js以外のプロジェクトでも知見が活かせる
- React、Next.js以外で触ったことがある場合はその経験を活かせる
デメリット
- 学習コストが非常に高い
- 単純なチャートでもコード量が多くなる
- 自前で多くの実装が必要
向いている案件
- ビジュアルに凝る必要がある場合
- D3.jsの経験がある場合
向いていない案件
- 標準的なチャートのみで十分な場合
3. Chart.js(react-chartjs-2)
概要
Chart.jsはJavaScriptの定番チャートライブラリで、react-chartjs-2はChart.jsをReactで使えるようにするものです。
Canvasベースの描画で、軽量かつ高速です。
特徴
- Canvasベースで描画が高速
- アニメーションがデフォルトで有効
- 8種類(Chartを含めたら9種類)の基本チャートタイプを提供
- プラグインシステムで拡張可能
コード例
'use client';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import { Bar } from 'react-chartjs-2';
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
const data = {
labels: ['1月', '2月', '3月', '4月', '5月', '6月'],
datasets: [
{
label: '売上',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: 'rgba(53, 162, 235, 0.5)',
},
],
};
export default function SalesBarChart() {
return <Bar data={data} />;
}
メリット
- 導入が非常に簡単
- Canvasベースのため大量のデータでも比較的高速
- アニメーションが綺麗
- React、Next.js以外のプロジェクトでも知見が活かせる
- React、Next.js以外で触ったことがある場合はその経験を活かせる
デメリット
- Canvasのため個々の要素へCSSでスタイルを当てることができない
- 複雑なカスタマイズには限界がある
向いている案件
- シンプルなチャートを素早く実装したい
- Chart.jsの経験がある場合
向いていない案件
- 細かいスタイリングが必要な場合
4. Nivo
概要
NivoはD3.jsベースのReact向けチャートライブラリで、デザイン性の高さとチャートタイプの豊富さが特徴です。
公式サイト: https://nivo.rocks/ (※ホスティングの問題で一時的にアクセスできない場合があります)
GitHub: https://github.com/plouc/nivo
特徴
- SVG、Canvas、HTMLの3つのレンダリング方式に対応
- SSRに対応
- themeプロパティでチャートのスタイルを一括設定可能
コード例
'use client';
import { ResponsiveBar } from '@nivo/bar';
const data = [
{ country: 'JP', sales: 120, profit: 80 },
{ country: 'US', sales: 90, profit: 60 },
{ country: 'EU', sales: 70, profit: 45 },
];
export default function NivoBarChart() {
return (
<div style={{ height: 400 }}>
<ResponsiveBar
data={data}
keys={['sales', 'profit']}
indexBy="country"
margin={{ top: 50, right: 130, bottom: 50, left: 60 }}
padding={0.3}
colors={{ scheme: 'nivo' }}
borderColor={{ from: 'color', modifiers: [['darker', 1.6]] }}
axisBottom={{ tickSize: 5, tickPadding: 5 }}
axisLeft={{ tickSize: 5, tickPadding: 5 }}
labelSkipWidth={12}
labelSkipHeight={12}
legends={[
{
dataFrom: 'keys',
anchor: 'bottom-right',
direction: 'column',
translateX: 120,
itemWidth: 100,
itemHeight: 20,
},
]}
/>
</div>
);
}
メリット
- デフォルトのデザインが綺麗
- チャートタイプが豊富
- SSRに対応している
- テーマ機能でダークモード対応も容易
デメリット
- propsが多く、初見では複雑に感じる
向いている案件
- デザインを重視するプロジェクト
- SSRが必要なプロジェクト
向いていない案件
- シンプルなチャートで十分な場合
5. Apache ECharts(echarts-for-react)
概要
Apache EChartsは中国のBaidu発のオープンソースチャートライブラリで、特に大規模データの処理に優れています。
- 公式サイト: https://echarts.apache.org/
特徴
- オプションオブジェクトによる宣言的設定
- Canvas/SVGの両方に対応しており、大規模データに強い
- 非常に豊富なチャートタイプ(3D、地図、Chord図、サンキー図など)
- ドリルダウンをサポート
- TypeScript対応
- 機能ごとに分割されたパッケージ構成で、必要な部分だけインポート可能
コード例
'use client';
import ReactECharts from 'echarts-for-react';
export default function EChartsDemo() {
const option = {
title: { text: '売上推移' },
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月'],
},
yAxis: { type: 'value' },
series: [
{
name: '売上',
type: 'line',
data: [150, 230, 224, 218, 135, 147],
smooth: true,
},
],
};
return <ReactECharts option={option} style={{ height: 400 }} />;
}
//ドリルダウン
'use client';
import ReactECharts from 'echarts-for-react';
import { useRef, useCallback } from 'react';
import type { ECharts } from 'echarts';
const drilldownData = [
{
dataGroupId: 'animals',
data: [
['Cats', 4],
['Dogs', 2],
['Cows', 1],
['Sheep', 2],
['Pigs', 1],
]
},
{
dataGroupId: 'fruits',
data: [
['Apples', 4],
['Oranges', 2],
]
},
{
dataGroupId: 'sports',
data: [
['Soccer', 5],
['Baseball', 3],
['Tennis', 2],
]
}
];
const getMainOption = () => ({
xAxis: {
data: ['Animals', 'Fruits', 'Sports']
},
yAxis: {},
dataGroupId: '',
animationDurationUpdate: 500,
series: {
type: 'bar',
id: 'sales',
data: [
{ value: 5, groupId: 'animals' },
{ value: 2, groupId: 'fruits' },
{ value: 4, groupId: 'sports' }
],
universalTransition: {
enabled: true,
divideShape: 'clone'
}
}
});
export default function DrilldownChart() {
const chartRef = useRef<ECharts | null>(null);
const onChartReady = useCallback((chart: ECharts) => {
chartRef.current = chart;
}, []);
const onEvents = {
click: (event: { data?: { groupId?: string } }) => {
if (!chartRef.current || !event.data) return;
const subData = drilldownData.find(
data => data.dataGroupId === event.data?.groupId
);
if (!subData) return;
chartRef.current.setOption({
xAxis: {
data: subData.data.map(item => item[0])
},
series: {
type: 'bar',
id: 'sales',
dataGroupId: subData.dataGroupId,
data: subData.data.map(item => item[1]),
universalTransition: {
enabled: true,
divideShape: 'clone'
}
},
graphic: [
{
type: 'text',
left: 50,
top: 20,
style: {
text: 'Back',
fontSize: 18,
fill: '#3498db',
cursor: 'pointer'
},
onclick: () => {
chartRef.current?.setOption(getMainOption(), true);
}
}
]
});
}
};
return (
<ReactECharts
option={getMainOption()}
style={{ height: 400 }}
onChartReady={onChartReady}
onEvents={onEvents}
/>
);
}
メリット
- 大量のデータ処理に強い
- チャートタイプが非常に豊富
リッチな感じのやつも沢山あります。
https://echarts.apache.org/examples/en/index.html#chart-type-line - 設定を1つのオブジェクトにまとめて渡すシンプルな方式で分かりやすい
- ドリルダウンなど高度な機能をサポート
デメリット
- バンドルサイズが大きい(Tree shakingで軽減可能)
向いている案件
- 大規模データの可視化
- 地図・3Dグラフが必要な場合
- ドリルダウンなど高度なインタラクションが必要
向いていない案件
- シンプルなチャートのみでいい場合
ライブラリの使い分け
迷ったらRecharts
シンプルで学習コストも低いため、チーム全員がすぐに使えて、ほとんどのユースケースをカバーできます。
大規模データやリッチな表現、高度な機能が必要ならECharts
大規模データを扱ったり、ドリルダウンのような高度な機能の使用、様々なタイプのチャートが必要な場合はEChartsが圧倒的に強いです。
デザイン性重視ならNivo
デフォルトでもデザイン性の高いスタイルは大きな強みです。
自由度の高さが必要ならD3.js
既存ライブラリで実現できない見た目を作りたい場合に最適です。
自由度は最も高いですが、その分学習コストも高いです。
まとめ
というわけで今回はNext.jsでも使えるチャートライブラリについて調べた内容をまとめてみました。
実務ではほとんどのケースでRechartsがバランスの良い選択になりそうですが、個人的に一番触っていて楽しいのはEChartsですね。
公式のExmaplesを見ているだけでも楽しいです。
皆さんもぜひ本記事を参考に、開発に合わせてライブラリを選んでみてください。
参考リンク
※記事中のサンプルコードは一部ライブラリの公式サンプルコードをベースにしています。








