ReactでRechartsを使って折れ線グラフと棒グラフを描く方法
Recharts は、React のコンポーネントを組み合わせてグラフを描くライブラリです。<LineChart> の中に軸・ツールチップ・凡例・線を並べるだけで、SVG の折れ線グラフになります。LineChart を BarChart に、Line を Bar に替えると、同じデータで棒グラフになります。
組み立て方は短く書けますが、大きさを指定しないと何も表示されないなど、つまずきやすい点もありました。グラフの大きさ、縦軸の目盛り、描くときのアニメーションの扱い方も、Chromium で測った値とともに示します。
検証環境は Ubuntu 24.04、Node.js 22.23.4 です。recharts 3.10.1、react / react-dom 19.3.1、@types/react 19.3.0、typescript 7.0.2、esbuild 0.28.2 を使い、.tsx を tsc で型チェックしてから esbuild でビルドしました。ブラウザは Playwright 1.63.0 で動かした Chromium 153(ヘッドレス)だけです。Firefox、Safari、実機のスマートフォン、Next.js などでのサーバーサイドレンダリング、古い Recharts で使う ResponsiveContainer は試していません。
Recharts と react-is を入れる
Recharts の README では、react-is もいっしょに入れるように書かれています。react-is は、使っている react と同じバージョンにそろえる必要があります。
npm install recharts react-is
検証環境の npm は、react-is を指定せずに recharts を入れたときにも、peerDependencies として react-is を自動で入れていました。入ったバージョンは、react と同じ 19.3.1 でした。
データは横軸の1目盛りごとに1つのオブジェクトにする
Recharts に渡すデータは、オブジェクトの配列です。1つのオブジェクトが、横軸の1目盛り(ここでは1か月)にあたります。線や棒にしたい値は、オブジェクトのキーで指定します。
例として、Web とアプリの月ごとの利用者数(架空の数字)を使います。
// data.ts
export type MonthlyVisits = {
month: string;
web: number;
app: number;
};
export const visits: MonthlyVisits[] = [
{ month: '4月', web: 1200, app: 800 },
{ month: '5月', web: 1500, app: 950 },
{ month: '6月', web: 1100, app: 1300 },
{ month: '7月', web: 1800, app: 1250 },
{ month: '8月', web: 2100, app: 1600 },
{ month: '9月', web: 1900, app: 1900 },
];
折れ線グラフを描く
LineChart に data を渡し、中に部品を並べます。
// VisitsLineChart.tsx
import {
CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis,
} from 'recharts';
import { visits } from './data';
export function VisitsLineChart() {
return (
<LineChart
data={visits}
responsive
style={{ width: '100%', maxWidth: 640, aspectRatio: 1.6 }}
margin={{ top: 16, right: 24, bottom: 0, left: 0 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis width={56} />
<Tooltip formatter={(value) => `${Number(value).toLocaleString()}人`} />
<Legend />
<Line type="monotone" dataKey="web" name="Web" stroke="#00c8e8" strokeWidth={2} />
<Line type="monotone" dataKey="app" name="アプリ" stroke="#9aa5ab" strokeWidth={2} />
</LineChart>
);
}
部品の役割は次のとおりです。
CartesianGrid:背景の格子線XAxis:横軸。dataKey="month"で、各オブジェクトのmonthを目盛りの文字にするYAxis:縦軸。目盛りは値の範囲から自動で決まるTooltip:グラフにマウスを乗せたときに、その月の値を出すLegend:凡例Line:線。dataKeyで、どのキーの値を線にするかを決める
Line の name は、ツールチップと凡例に出す名前です。Line の API リファレンスのとおり、name を省くと dataKey の値(web)がそのまま出ます。type の既定値は linear です。type="monotone" を付けると、下の画像のように点と点が曲線でつながりました。
Tooltip の formatter は、ツールチップに出す値を書き換える関数です。ここでは3桁ごとにカンマを入れ、「人」を付けました。グラフの中の7月の位置にマウスを乗せると、ツールチップは「7月」「Web : 1,800人」「アプリ : 1,250人」になりました。

同じデータで棒グラフにする
棒グラフにするには、LineChart を BarChart に、Line を Bar に替えます。棒の色は、Line の stroke の代わりに fill で指定します。fill="#00c8e8" を渡した棒は、その色で塗られました。軸やツールチップなどの部品は、そのまま使えます。
// VisitsBarChart.tsx
import {
Bar, BarChart, CartesianGrid, Legend, Tooltip, XAxis, YAxis,
} from 'recharts';
import { visits } from './data';
type Props = { stacked?: boolean };
export function VisitsBarChart({ stacked = false }: Props) {
const stackId = stacked ? 'total' : undefined;
return (
<BarChart
data={visits}
responsive
style={{ width: '100%', maxWidth: 640, aspectRatio: 1.6 }}
margin={{ top: 16, right: 24, bottom: 0, left: 0 }}
>
<CartesianGrid strokeDasharray="3 3" vertical={false} />
<XAxis dataKey="month" />
<YAxis width={56} />
<Tooltip formatter={(value) => `${Number(value).toLocaleString()}人`} />
<Legend />
<Bar dataKey="web" name="Web" fill="#00c8e8" stackId={stackId} />
<Bar dataKey="app" name="アプリ" fill="#9aa5ab" stackId={stackId} />
</BarChart>
);
}
Bar を2つ並べると、1か月の中に Web とアプリの棒が横に並びます。横幅640pxのグラフで測ると、どちらの棒も幅35pxで、4pxのすきまを空けて並びました。マウスを乗せると、その月の後ろに灰色(#ccc)の帯が出ました。これは Tooltip が描くカーソルで、Tooltip の API リファレンスによると、cursor={false} を付けると描かれなくなります。

2つの棒を積み上げる
Bar に同じ stackId を渡すと、棒が積み上がります。Bar の API リファレンスでは、軸と stackId が同じ Bar どうしが積み上がると説明されています。上のコードでは、stacked が true のときだけ、2つの Bar に 'total' を渡しています。
積み上げると、縦軸の目盛りは合計の値に合わせて変わりました。並べたときは0〜2200でしたが、積み上げると0〜3800になりました。9月の合計(1,900+1,900)が3,800だからです。

グラフの大きさを決める
Recharts のグラフは、大きさを指定しないと何も描きません。公式ガイドのグラフの大きさの説明にも、そう書かれています。<LineChart data={visits}> だけで描いたところ、出力されたのは中身が空の <div class="recharts-wrapper"> だけでした。エラーや警告は出ませんでした。
大きさの決め方は、主に2つあります。
1つは、width={640} height={400} のように数値で指定する方法です。この場合、グラフはいつも 640×400px で描かれます。ブラウザの幅を800pxから375pxに縮めても640pxのままで、ページの横幅が656pxになり、横にスクロールが出ました。
もう1つは、この記事のコードのように responsive を付け、style で CSS の大きさを指定する方法です。width: '100%' で親の幅いっぱいに広げ、maxWidth で大きな画面での上限を決め、aspectRatio で縦横の比を保ちます。ブラウザの幅を375pxに縮めると、グラフは 640×400px から 343×214px に縮みました。ページの横幅は375pxのままでした。
スマートフォンでも見せるグラフには、responsive と style の組み合わせが向いています。
公式ガイドによると、responsive は Recharts 3.3 で入りました。それより前のバージョンでは、ResponsiveContainer で囲むと、似たように大きさを合わせられます(公式ガイドでは、responsive ほど柔軟ではないとされています)。その場合は、ResponsiveContainer の親要素に大きさを決めておく必要があります。
縦軸の目盛りをきりのよい数にする
何も指定しないと、このデータの縦軸の目盛りは 0、550、1100、1650、2200 になりました。区切りが550では、値を読み取りにくくなります。
YAxis に domain で範囲を、ticks で目盛りの値を渡すと、目盛りを決められます。
<YAxis width={56} domain={[0, 2500]} ticks={[0, 500, 1000, 1500, 2000, 2500]} />
この1行だけを替えたところ、目盛りは 0、500、1000、1500、2000、2500 になりました。なお、domain={[0, 2500]} だけを付けたときは 650 刻み、tickCount={6} だけを付けたときは 450 刻みで、どちらもきりのよい数にはなりませんでした(この2つは、横軸のないグラフで確かめた値です)。
描くときのアニメーションを止める
Recharts は、最初に描くときに線や棒を少しずつ伸ばします。API リファレンスによると、かかる時間(animationDuration)の既定値は、Line が1,500ミリ秒、Bar が400ミリ秒です。
軸を付けない、線1本だけの 640×400px の折れ線グラフで、線の長さ(726px)のうち表示されている部分を測りました。表示を始めてから100ミリ秒後には約20px、500ミリ秒後には約388px、1,000ミリ秒後には約663pxで、1,700ミリ秒後には線全体が出ていました。
画面の画像を撮るテストなどで、描き終わるのを待ちたくないときは、isAnimationActive={false} を付けます。付けると、100ミリ秒後にはもう線全体が出ていました。
<Line dataKey="web" isAnimationActive={false} />
isAnimationActive の既定値は 'auto' です。API リファレンスでは、'auto' のときはサーバーサイドレンダリングでアニメーションをしないことと、OS などで動きを減らす設定(prefers-reduced-motion)をしているかどうかに従うことが説明されています。
Chromium で prefers-reduced-motion: reduce にして試したところ、isAnimationActive を付けていない折れ線グラフも棒グラフも、150ミリ秒後には描き終わっていました。この設定をしていないときは、150ミリ秒の時点で、棒の高さは描き終わったときの180pxに対して89pxでした。





