tech-book-labs
データ基盤(クライアント完結) · 最終検証 2026-09-14 · exceljs 4.4.0 · 初公開 2026-05-10

ExcelJS の使い方: ブラウザで .xlsx を生成・ダウンロードする

ExcelJS とは何かから、装飾(太字・背景色・罫線・セル結合)、数式、数値書式、ダウンロードまでを動く demo と最小コードで。10 万行での SheetJS とのサイズ・速度の実測と、はまりやすい 7 つの落とし穴つき。

exceljs xlsx spreadsheet browser

検証日: 2026-09-14(初版 2026-05-10)

使用バージョン: exceljs@4.4.0(2026-05 時点で latest)

対象: ブラウザでユーザに .xlsx をダウンロードさせたい / Node 上で帳票生成したい場面

ExcelJS で .xlsx をブラウザ完結で生成・ダウンロード するパターン。書式付きセル / 数式 / 複数シート / 画像埋め込み / Node 出力までを動く demo で確認します。

触って試す

ヘッダ装飾 + 数式 (=C2*D2 / =SUM) + 通貨書式 ¥ 付きで Excel ファイルを生成。

ボタンを押すと、装飾ヘッダ + 数式 + 通貨書式付きの xlsx が生成されてダウンロードされる。

この実装を体系立てて学ぶ本(ブラウザでファイルを作る処理は、Blob / File API と非同期処理の理解で安定する)

tech-book.net /books/9784297132880

改訂3版JavaScript本格入門

山田 祥寛 · 技術評論社 · 2023年

ES2022対応の文法基礎からオブジェクト指向構文まで一冊で体系的に押さえる

この本が役立つ理由 — Blob・File・非同期処理(Promise / async)を基礎から。ブラウザ完結のファイル生成で詰まる箇所がこの本の範囲
詳細を tech-book.net で見る
tech-book.net /books/9784297129569

実践Node.js入門ー基礎・開発・運用

伊藤康太 · 技術評論社 · 2023年

Node.jsの基礎文法から非同期処理・CLI・Express実装まで一気通貫で学ぶ。フロントエンド開発の経験があり、Node.jsでのサーバーサイド開発を体系的に学び直したい人向け。

この本が役立つ理由 — 同じ生成処理を Node.js のサーバ側でも動かすとき(Buffer と Stream の扱い)
詳細を tech-book.net で見る

ExcelJS とは、xlsx 生成とは(30 秒版)

ExcelJS は、JavaScript だけで Excel ファイル(.xlsx)を 作る・読む・書き換える ためのライブラリ。Node.js でもブラウザでも同じ API で動き、セルの値だけでなく、太字・背景色・罫線・数値書式・数式・結合セル・列幅といった Excel 上の見た目と振る舞い まで指定できる。

xlsx を生成する とは、内部的には Office Open XML という規格の XML ファイル群を組み立てて zip にまとめること。ExcelJS はその組み立てを全部引き受けるので、書く側は「シートに行を足す」「このセルを太字にする」というオブジェクト操作だけで済む。Excel 本体も、サーバ側の Office も要らない。

読み込み専用でよい、または速度と軽さを優先するなら SheetJS(xlsx)も選択肢になる。違いは末尾の「100,000 行で実測」と「向く / 向かないケース」にまとめた。

セットアップ(npm 1 行 / Node + Browser 同 API)

npm i exceljs
項目値
公開バージョン4.4.0
Node 必要バージョン18+
TypeScript 型本体同梱(別途 @types/exceljs は不要、deprecated)
ブラウザ対応UMD bundle 同梱(exceljs/dist/exceljs.min.js)、ESM でも import 可
バンドル影響min+gzip で 200KB 前後(フォントサブセット内部リソースあり)、main bundle に同梱せず動的 import 推奨
ライセンスMIT

ブラウザで使うなら import ExcelJS from "exceljs"。Vite / webpack で警告が出る場合は optimizeDeps.include に exceljs を入れる。Cloudflare Workers のような Web API しかない実行環境 では fs 依存の writeFile は使えないので、writeBuffer() + Response で配信する形になる(後述)。

最小サンプル(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 でダウンロード。

装飾 2 種(ヘッダ太字 + 背景色 ARGB)

ヘッダ行の 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 とは桁数が違うので注意。

数式 + 集計(SUM の 2 パターン)

セル値の代わりに { 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 が開いた時に値が自動計算される。

数値書式 3 種(通貨 / パーセント / 日付)

列単位で numFmt(Excel の番号書式コード)を当てると、各セルの表示が一括で揃う:

sheet.getColumn("price").numFmt = '"¥"#,##0';
sheet.getColumn("rate").numFmt = "0.00%";
sheet.getColumn("date").numFmt = "yyyy/mm/dd";

Excel の番号書式コードがそのまま使える。

罫線 4 辺 + mergeCells

全セルに罫線を引いて、ヘッダ行は 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");

装飾した Excel をダウンロードする(完成例)

ここまでの装飾(ヘッダの太字と背景色、罫線、数値書式、折り返し、列幅)を 1 つにまとめ、ブラウザでダウンロードさせるまでの完成形。writeBuffer() の戻り値は Node なら Buffer、ブラウザなら ArrayBuffer だが、Blob はどちらも受け取れる。

import ExcelJS from "exceljs";

export async function downloadStyledXlsx(rows: { item: string; qty: number; date: Date }[]) {
  const wb = new ExcelJS.Workbook();
  const ws = wb.addWorksheet("data");
  ws.columns = [
    { header: "品目", key: "item", width: 28 },
    { header: "数量", key: "qty", width: 10 },
    { header: "日付", key: "date", width: 12 },
  ];
  ws.addRows(rows);

  const header = ws.getRow(1);
  header.font = { bold: true };
  header.fill = { type: "pattern", pattern: "solid", fgColor: { argb: "FFE2E8F0" } };
  header.alignment = { vertical: "middle", horizontal: "center" };

  const thin = { style: "thin" as const };
  ws.eachRow((row, n) => {
    row.eachCell((cell, c) => {
      cell.border = { top: thin, left: thin, bottom: thin, right: thin };
      if (n === 1) return;
      if (c === 1) cell.alignment = { wrapText: true };   // 長い品目名は折り返す
      if (c === 2) cell.numFmt = "#,##0";                  // 3 桁区切り
      if (c === 3) cell.numFmt = "yyyy-mm-dd";             // Date をそのまま渡し、表示だけ書式で決める
    });
  });

  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 = Object.assign(document.createElement("a"), { href: url, download: "report.xlsx" });
  a.click();
  URL.revokeObjectURL(url);
}

装飾はどれくらい重いのか。1,000 行 × 10 列を「装飾なし」「ヘッダのみ」「全セルに罫線 + 書式 + 折り返し」の 3 通りで書き出し、生成時間とサイズを測った(Node 22.17、exceljs@4.4.0、3 回の中央値、2026-09-14。計測スクリプト)。

テーブルで選択した行だけを出力したい場合は、TanStack Table v9 の「選択した行だけを xlsx に出す」 に、選択状態のどこから行を取るかで出力行数が 120 行にも 1,000 行にもなる実測があります。

CSV ↔ xlsx の 2 方向変換

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");

Cloudflare Workers / Edge で 1 endpoint .xlsx 配信

exceljs の writeBuffer() は Buffer | ArrayBuffer を返すので、Node fs を経由せず Response にそのまま流せる:

export async function onRequestGet() {
  const ExcelJS = await import("exceljs");
  const wb = new ExcelJS.Workbook();
  const sheet = wb.addWorksheet("Report");
  sheet.addRow(["id", "name"]);
  sheet.addRow([1, "Alice"]);

  // Node では Buffer、ブラウザ / Workers では ArrayBuffer
  const buf = await wb.xlsx.writeBuffer();

  return new Response(buf, {
    headers: {
      "Content-Type":
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      "Content-Disposition": 'attachment; filename="report.xlsx"',
    },
  });
}

Workers のサイズ制限(1MB scripts、25MB free / 100MB paid)を超えないように、exceljs は dynamic import で route 単位にロードするのが安全。fonts / 画像など重い機能を使わなければ実用範囲に収まる。

7 つの落とし穴と直し方

症状原因直し方
塗りが反映されないARGB の 先頭 2 桁が alphaHTML hex E2E8F0 の前に FF を付けて FFE2E8F0 にする
配列 index で行を指定すると ずれるCell reference は 1-based(C2 = 2 行目)sheet.getRow(1) がヘッダ、getRow(2) が 1 行目データ
writeBuffer() の戻り値が型エラー環境で Buffer (Node) / ArrayBuffer (Browser) が違うBlob([buf]) は両方受け取るので Blob 化してから扱う
数式の参照がズレるaddRow で動的に行が増えると数式の絶対セル参照も追従しないformula を文字列補完で組み立てる(sheet.rowCount で最終行を取って SUM(C2:C + n + ) のように生成)
mergeCells 後に値を入れたのに表示されないマージ後は 左上セル以外の値が無視されるmergeCells する前に値を入れるか、左上セル getCell("A1") に書く
通貨が ¥1,500.00 になってしまうnumFmt のコード末尾に小数指定'"¥"#,##0'(整数)/ '"¥"#,##0.00'(小数 2 桁)を使い分け
10 万行で OOMworkbook を全部 memory に保持wb.xlsx.write(stream) で書き出し(Node)、または xlsx-populate / node-xlsx 等の軽量代替

100,000 行で実測: サイズと速度(ExcelJS vs SheetJS)

5 列 × 100,000 行を同じデータで書き出し、読み戻した(Node 22.17、exceljs@4.4.0 / xlsx@0.20.3、3 回の中央値、2026-09-05)。

評価

観点評価コメント
学習コスト○API は分かりやすいが、書式コード仕様(numFmt)に Excel 知識必要
機能網羅◎装飾 / 数式 / グラフ / 画像 / mergeCells すべて対応
ブラウザ対応◎Node / browser 同 API
バンドル△200KB 前後、フォントサブセットなどの内部リソース大きい
TypeScript○型同梱、ただし一部 any

向く / 向かないケース

  • 向く: 帳票出力、レポート PDF と並ぶ業務系出力、ユーザダウンロード機能
  • 向かない: シンプルな CSV 出力(Papa.unparse のほうが軽い)
  • 向かない: 100 万行超(streaming write or 別ツール)。100,000 行で書き出し 1.8 秒(上の実測)なので、その 10 倍は UI スレッドでは無理

関連 Topic / 関連書籍

この記事と関係する tech-book.net の Topic と、それぞれの Topic に紐づく書籍:

tech-book.net /books/9784297132880

改訂3版JavaScript本格入門

山田 祥寛 · 技術評論社 · 2023年

ES2022対応の文法基礎からオブジェクト指向構文まで一冊で体系的に押さえる

この本が役立つ理由 — Blob・File・非同期処理を基礎から。ブラウザ完結のファイル生成で詰まる箇所がこの本の範囲
詳細を tech-book.net で見る
tech-book.net /books/9784297129569

実践Node.js入門ー基礎・開発・運用

伊藤康太 · 技術評論社 · 2023年

Node.jsの基礎文法から非同期処理・CLI・Express実装まで一気通貫で学ぶ。フロントエンド開発の経験があり、Node.jsでのサーバーサイド開発を体系的に学び直したい人向け。

この本が役立つ理由 — 同じ生成処理を Node.js のサーバ側でも動かすとき(Buffer と Stream)
詳細を tech-book.net で見る
tech-book.net /books/9784297127473

プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで

鈴木 僚太 · 技術評論社 · 2022年

TypeScriptの型システムを基礎から高度な表現力まで体系的に習得する

この本が役立つ理由 — 型付きで書くなら。ライブラリの型定義を読み解く力がつく
詳細を tech-book.net で見る
tech-book.net /books/9784627857216

はじめてのWebデザイン&プログラミング : HTML、CSS、JavaScript、PHPの基本

村上 祐治 · 森北出版 · 2023年

HTML・CSS・JavaScript・PHP を横断してWebの全体像を最短で把握する

詳細を tech-book.net で見る
tech-book.net /books/9784297138714

フロントエンドの知識地図ーー 一冊でHTML/CSS/JavaScriptの開発技術が…

株式会社ICS 池田 泰延/西原 翼/松本 ゆき

フロントエンド技術の全体像を俯瞰し、学習の優先順位を自分で判断できるようにする

詳細を tech-book.net で見る