ExcelJS で .xlsx をブラウザで生成・ダウンロードする
ExcelJS でブラウザ完結に Excel ファイルを作成し、ヘッダ装飾 / 数式 / 通貨書式 / 集計行を最小コードで組み立てるパターンを触れる demo で確認する実装メモ。
検証日: 2026-05-10
使用バージョン:
exceljs@4.4.0対象: ブラウザでユーザに
.xlsxをダウンロードさせたい / Node 上で帳票生成したい場面
ExcelJS で .xlsx をブラウザ完結で生成・ダウンロード するパターン。書式付きセル / 数式 / 複数シート / 画像埋め込み / Node 出力までを動く demo で確認します。
触って試す
ボタンを押すと、装飾ヘッダ + 数式 + 通貨書式付きの xlsx が生成されてダウンロードされる。
最小サンプル(ExcelJS 4.4、ブラウザで 5 ステップ)
ワークブックを 1 つ作って、ヘッダ + 行を流し込み、Blob 化して .xlsx をダウンロードするまでの最小コード:
import ExcelJS from "exceljs";
const wb = new ExcelJS.Workbook();
const sheet = wb.addWorksheet("Report");
sheet.columns = [
{ header: "ID", key: "id", width: 8 },
{ header: "Name", key: "name", width: 24 },
{ header: "Total", key: "total", width: 12 },
];
sheet.addRow({ id: 1, name: "Alice", total: 1500 });
sheet.addRow({ id: 2, name: "Bob", total: 1100 });
const buf = await wb.xlsx.writeBuffer();
const blob = new Blob([buf], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "report.xlsx";
a.click();
URL.revokeObjectURL(url);
writeBuffer() で ArrayBuffer を取得 → Blob でダウンロード。
装飾(ヘッダ太字 + 背景色)
ヘッダ行の font を太字に、塗りつぶしを pattern: "solid" で指定:
sheet.getRow(1).font = { bold: true };
sheet.getRow(1).fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FFE2E8F0" }, // 8 桁 ARGB(先頭は alpha = FF)
};
ARGB の先頭 2 桁は alpha(FF = 不透明)。HTML の hex とは桁数が違うので注意。
数式 + 集計
セル値の代わりに { formula: "..." } を渡すと、Excel で開いた時に計算式として扱われる:
sheet.addRow({
id: 1, qty: 5, price: 200,
total: { formula: "C2*D2" }, // セル参照
});
sheet.addRow({
id: "", qty: { formula: "SUM(C2:C10)" }, price: "",
total: { formula: "SUM(E2:E10)" },
});
{ formula: "..." } で数式を埋める。Excel が開いた時に値が自動計算される。
数値書式(通貨 / パーセント / 日付)
列単位で numFmt(Excel の番号書式コード)を当てると、各セルの表示が一括で揃う:
sheet.getColumn("price").numFmt = '"¥"#,##0';
sheet.getColumn("rate").numFmt = "0.00%";
sheet.getColumn("date").numFmt = "yyyy/mm/dd";
Excel の番号書式コードがそのまま使える。
罫線 / マージ
全セルに罫線を引いて、ヘッダ行は mergeCells で 1 行ぶん結合:
// すべてのセルに罫線
sheet.eachRow((row) => {
row.eachCell((cell) => {
cell.border = {
top: { style: "thin" }, bottom: { style: "thin" },
left: { style: "thin" }, right: { style: "thin" },
};
});
});
// セルマージ
sheet.mergeCells("A1:C1");
CSV ↔ xlsx 変換
wb.csv / wb.xlsx でフォーマット間を相互変換できる(Node 想定、stream API):
// CSV を xlsx に
const wb = new ExcelJS.Workbook();
await wb.csv.read(stream); // Node の Readable Stream
await wb.xlsx.writeFile("out.xlsx");
// xlsx を CSV に
await wb.xlsx.readFile("in.xlsx");
await wb.csv.writeFile("out.csv");
つまずいたポイント
- ARGB の桁数:
FFE2E8F0(alpha 含む)、E2E8F0(HTML hex)を混同しがち - Cell reference の 1-based:
C2の2は 2 行目(配列 index と違う) writeBuffer()の戻り値:browser ではArrayBuffer、Node ではBuffer。型 import は同じだが Blob 化する時に注意- 大きいファイルでのメモリ:全行を memory に乗せるので、10 万行超なら streaming write を使うべき
- マージ後のセル参照:マージしたら左上以外のセルへの値設定は無視される
評価
| 観点 | 評価 | コメント |
|---|---|---|
| 学習コスト | ○ | API は分かりやすいが、書式コード仕様(numFmt)に Excel 知識必要 |
| 機能網羅 | ◎ | 装飾 / 数式 / グラフ / 画像 / mergeCells すべて対応 |
| ブラウザ対応 | ◎ | Node / browser 同 API |
| バンドル | △ | 200KB 前後、フォントサブセットなどの内部リソース大きい |
| TypeScript | ○ | 型同梱、ただし一部 any |
向く / 向かないケース
- 向く: 帳票出力、レポート PDF と並ぶ業務系出力、ユーザダウンロード機能
- 向かない: シンプルな CSV 出力(Papa.unparse のほうが軽い)
- 向かない: 100 万行超(streaming write or 別ツール)
関連 Topic / 関連書籍
この記事と関係する tech-book.net の Topic と、それぞれの Topic に紐づく書籍:
JavaScriptによるはじめてのアルゴリズム入門
Python と JavaScriptではじめるデータビジュアライゼーション
React Native+Expoではじめるスマホアプリ開発 : JavaScriptによるアプリ構築の実際
「React Native」は、Facebookが開発しているスマートフォンアプリ向けの開発環境です。ほとんどのコードをJav…