Recharts のグラフを PowerPoint に出力する — ネイティブグラフと画像、2 通りの書き出しを比べる
React(Recharts)で画面に描いた棒グラフを、PptxGenJS で .pptx に書き出す 2 つの方法。同じデータを addChart で PowerPoint 側で編集できるグラフとして置く方法と、SVG を PNG にして addImage で貼る方法を、同じデモで実測して比べる。CSS 変数の解決、HTML の凡例が画像に入らない問題、フォント非埋め込みまで実装メモ。
検証日: 2026-09-14
使用バージョン:
recharts@3.8.1/pptxgenjs@4.0.1/ React 19対象: ダッシュボードに出しているグラフを「そのまま PowerPoint に貼りたい」と言われた人。データを PowerPoint のグラフに写すか、画面を画像にするかで迷っている人
画面に Recharts で描いた棒グラフを、ブラウザだけで .pptx に書き出す。方法は 2 つある。同じデータを PptxGenJS の addChart に渡して PowerPoint 側のグラフとして置くか、描画済みの SVG を PNG にして addImage で貼るか。前者は受け取った人が PowerPoint で数値も色も直せる。後者は画面と同じ見た目になるが画像なので直せない。両方を 1 つのデモに入れて、サイズと時間を測った。
触って試す
CSV を書き換えるとグラフが変わる。2 つのボタンでそれぞれ .pptx を生成してダウンロードし、下の表にサイズと所要時間が出る。生成した 2 つのファイルを PowerPoint で開き、グラフをダブルクリックすると違いが分かる(ネイティブは「データの編集」が開く、画像は「図の書式設定」になる)。
どちらを選ぶか
- 受
- け
- P
- o
- デ
- ー
- 対
- 応
- 画
- 面
- 編
- 集
- 描
- 画
- 解
- 像
判断の軸は「渡した先で直すか」。経営会議の資料のように数字が後から動く用途はネイティブ、監査や報告書のように見た目を固定したい用途は画像、と分けるとぶれない。
1. ネイティブグラフ: データを写す
Recharts に渡している配列を、PptxGenJS の系列形式({ name, labels, values }[])に組み替えて slide.addChart に渡すだけでよい。画面の DOM は使わない。
const { default: PptxGenJS } = await import("pptxgenjs");
const pptx = new PptxGenJS();
pptx.layout = "LAYOUT_WIDE";
const slide = pptx.addSlide();
// Recharts の data: [{ label: "Jan", visits: 120, signups: 28 }, ...]
const series = ["visits", "signups"].map((key) => ({
name: key,
labels: data.map((d) => d.label),
values: data.map((d) => Number(d[key])),
}));
slide.addChart(pptx.ChartType.bar, series, {
x: 0.6, y: 0.6, w: 12.1, h: 6.3,
barDir: "col", // 縦棒
grouping: "clustered", // 系列を横に並べる(積み上げは "stacked")
showLegend: true, legendPos: "b",
chartColors: ["0F6E63", "5A574F"], // 系列色は HEX 6 桁(# なし)
valGridLine: { color: "D9D4C8", width: 1 },
});
const blob = await pptx.write({ outputType: "blob" });
画面と色を揃えたい場合、Recharts 側の色が CSS 変数(var(--color-accent) など)だと PptxGenJS には渡せない。デモでは <span style="color: var(--color-accent)"> を一瞬 DOM に置いて getComputedStyle で rgb() を取り、canvas に塗って HEX に変換している。テーマ切替(ライト / ダーク)にそのまま追従する。
2. 画像: SVG を PNG にして貼る
Recharts の描画は <svg class="recharts-surface">。これを XMLSerializer で文字列にし、<img> に data URL として読み込み、canvas に描いて toDataURL("image/png") で PNG にする。手順は 4 段で、詰まりどころは 2 つ目にある。
getComputedStyle(el).getPropertyValue('fill') 外部 CSS と CSS 変数は data URL の中では効かない。fill / stroke / font 系を要素ごとに書き戻す <Legend content={() => <svg data-export-legend>…</svg>} /> Recharts の Legend は HTML(div)なので SVG に入らない。SVG で自作した凡例を差し込む canvas.width = w * devicePixelRatio * 2 倍率はここで決まる。等倍だと PowerPoint 上でぼやける slide.addImage({ data, x, y, w, h }) 縦横比は SVG の bounding box から計算して合わせる const surface = root.querySelector<SVGSVGElement>("svg.recharts-surface")!;
const clone = surface.cloneNode(true) as SVGSVGElement;
// 元要素と複製を同じ順に走査し、計算済みの fill / stroke / font を style 属性へ
[surface, ...surface.querySelectorAll("*")].forEach((el, i) => {
const copy = [clone, ...clone.querySelectorAll("*")][i] as SVGElement;
const cs = getComputedStyle(el);
for (const p of ["fill", "stroke", "stroke-width", "font-family", "font-size", "opacity"]) {
copy.style.setProperty(p, cs.getPropertyValue(p));
}
});
const svgText = new XMLSerializer().serializeToString(clone);
const img = new Image();
await new Promise((ok, ng) => { img.onload = ok; img.onerror = ng; img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgText); });
const scale = (devicePixelRatio || 1) * 2;
const canvas = Object.assign(document.createElement("canvas"), { width: Math.ceil(w * scale), height: Math.ceil(h * scale) });
canvas.getContext("2d")!.drawImage(img, 0, 0, canvas.width, canvas.height);
slide.addImage({ data: canvas.toDataURL("image/png"), x: 0.6, y: 0.6, w: 12.1, h: 12.1 * h / w });
3. 実測: サイズと時間
同じ 6 行 × 2 系列のデータで両方を生成し、Blob のサイズと、ライブラリ読み込み後から Blob 完成までの時間を測った(Chromium、pptxgenjs@4.0.1、2026-09-14、3 回の中央値、倍率 2、計測スクリプト。数字はデモの表と同じ計測)。
つまずいたポイント
chartColorsは HEX 6 桁で#なし。rgb()や CSS 変数は無視されて既定色になる。grouping: "stacked"にすると積み上げになる。Recharts でstackIdを付けているグラフを写すときはここを合わせる。addImageのw/hは inch。ピクセルの縦横比だけ保てばよく、絶対値はLAYOUT_WIDEの 13.333 × 7.5 inch に収まるように決める。- canvas の倍率を上げるとサイズが増える。倍率 2 で十分に見え、4 にすると PNG が 4 倍近くになる。
向く / 向かないケース
- 向く: 週次・月次で同じ形のグラフを PowerPoint に流し込む運用。ネイティブなら受け取り側が数字を直せるので、差し戻しが減る。
- 向く: Recharts のカスタム描画(独自のラベルや注釈)をそのまま資料に載せたい場合は画像方式。
- 向かない: 画像方式をサーバで作りたい場合。DOM と canvas が要るので、サーバなら Recharts を使わずネイティブ方式でデータから作る。
関連
- pptxgenjs でブラウザだけで .pptx を生成する — スライド・表・画像・グラフの基本と、要素ごとのサイズの実測
- Recharts v3 ガイド — 棒グラフのレシピと積み上げ・複合グラフ
- ExcelJS で xlsx を生成する — 同じデータを Excel に出す側