📁

Next.jsでExcelファイルを出力する方法

2024/11/28に公開

はじめに

在庫管理システムなどを開発している場合、Excelファイルを出力したい場面は多々あります。今回はNext.jsを利用して、Excelファイルを出力する方法について解説していきます。

使用ライブラリ

Excelファイルを出力するためのライブラリは複数ありますが、今回はその中からSheet.jsを選択しました。Sheet.jsとは、JavaScriptでExcelファイルを扱うためのライブラリで、Excelファイルの読み込み・編集・エクスポートを可能にします。

Sheet.jsを選定した理由としては以下の通りです。

  • 定期的なメンテナンス
  • ドキュメントが充実
  • ダウンロード数の多さ

一方でデメリットもあります。Sheet.jsではExcelファイルをスタイリングすることができないため、Excelファイルを出力したい場合は他のライブラリ(ExcelJS / xlsx-populate)を検討する必要があります。

ライブラリの比較に関しては以下の記事にまとまっています。

https://qiita.com/nakahara-d/items/2506f90ce8081445b509

実装方法

実装方法は至ってシンプルです。処理の流れとしては次の通りです。

  1. データの取得(今回はスタブデータで代用)
  2. Excel上のデータ配置設定
  3. ワークシート・ワークブック作成
  4. ワークシート・ワークブックに対してデータの割り当て
app/page.tsx
"use client";

import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { SummaryCards } from "@/components/delivery/SummaryCards";
import { DeliveryTable } from "@/components/delivery/DeliveryTable";
import { Download } from "lucide-react";
import { useCallback } from "react";
import { DeliveryData } from "@/lib/types/delivery";
import { downloadExcel, formatDataForExcel } from "@/lib/utils/excel";

export default function Home() {
  // 表示するスタブデータの定義(適宜APIのレスポンスデータ等に置き換える)
  const deliveryData: DeliveryData = {
    totalWeight: 1000,
    totalQuantity: 3000,
    items: [
      {
        date: "2024/11/24",
        productName: "パイプ",
        quantity: 1500,
        weight: 500,
      },
      {
        date: "2024/11/24",
        productName: "スチール",
        quantity: 1500,
        weight: 500,
      },
    ],
  };
  // イベントハンドラー処理でExcelファイルをダウンロード(詳細はutils参照)
  const handleDownload = useCallback(() => {
    const excelData = formatDataForExcel(deliveryData);
    downloadExcel(excelData, "delivery-data.xlsx");
  }, [deliveryData]);

  return (
    <div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
      <div className="max-w-4xl mx-auto">
        <Card className="p-6">
          <div className="flex justify-between items-center mb-6">
            <h1 className="text-2xl font-semibold text-gray-900">在庫データ</h1>
            <Button
              onClick={handleDownload}
              className="flex items-center gap-2"
            >
              <Download className="h-4 w-4" />
              Excelダウンロード
            </Button>
          </div>

          <div className="space-y-6">
            <SummaryCards
              totalWeight={deliveryData.totalWeight}
              totalQuantity={deliveryData.totalQuantity}
            />
            <DeliveryTable items={deliveryData.items} />
          </div>
        </Card>
      </div>
    </div>
  );
}
  • 出力したいデータをdeliveryDataとして定義します。APIのレスポンス値などをExcelデータで出力したい場合は、適宜こちらを書き換えてください。
  • handleDownload関数を定義し、ボタンが押下されたら、utilsファイル内で定義したSheet.jsの機能を利用してExcelファイルをダウンロードします。
lib/utils/excel.ts
import * as XLSX from "xlsx";
import { DeliveryData, ExcelRow } from "../types/delivery";

// カラムの幅設定
const COLUMN_WIDTHS = [
  { wch: 12 },  // 納入日
  { wch: 15 },  // 商品名
  { wch: 10 },  // 数量
  { wch: 10 },  // 重量
];

// Excelシート内で表示したいデータを2次元配列上にマッピング
export const formatDataForExcel = (data: DeliveryData): ExcelRow[] => {
  return [
    ["重量合計", data.totalWeight],
    ["数量合計", data.totalQuantity],
    [],
    ["納入日", "商品名", "数量", "重量"],
    ...data.items.map((item) => [
      item.date,
      item.productName,
      item.quantity,
      item.weight,
    ]),
  ];
};

// ワークブック・ワークシートを作成し、データを割り当てる
export const downloadExcel = (data: ExcelRow[], filename: string) => {
  // ワークブックの作成
  const wb = XLSX.utils.book_new();
  // ワークシートの作成とデータ適用
  const ws = XLSX.utils.aoa_to_sheet(data);

  // カラム幅の設定
  ws["!cols"] = COLUMN_WIDTHS;

  // アラインメントの設定
  for (let i = 5; i < data.length + 1; i++) {
    ws[`A${i}`].s = { alignment: { horizontal: "left" } };
    ws[`B${i}`].s = { alignment: { horizontal: "left" } };
  }

  // ワークブックにワークシートを追加
  XLSX.utils.book_append_sheet(wb, ws, "在庫データ");

  // ファイルの書き込み
  XLSX.writeFile(wb, filename);
};
  • formatDataForExcel関数にてデータを2次元配列上にマッピングします。このインデックス位置がそのままExcelシートに反映されます。
  • downloadExcel関数では以下の3点を行います。
    • ワークブック・シートの作成
    • 2次元配列データの書き込み
    • Excelの横幅適用

デモ

成果物は以下になります。

https://astonishing-valkyrie-bd602b.netlify.app/

おわりに

今回はNext.js + Sheet.jsを用いたExcelファイル出力の方法について解説しました。上述した通り、デザイン面での制限があるため、興味がある方は別のライブラリで試してみることをお勧めします。一方で、Sheet.jsでも簡単にExcelファイル出力が実現できるため、要件に応じて選択するのが良いと思います。

参考資料

https://qiita.com/nakahara-d/items/2506f90ce8081445b509

https://npmtrends.com/excel4node-vs-exceljs-vs-node-xlsx-vs-officegen-vs-xlsx-vs-xlsx-js-style-vs-xlsx-populate

https://docs.sheetjs.com/docs/

Discussion