ReoGrid ReoGrid Web

脱・神Excel ― 方眼紙でつくった帳票を、作り直さずWebアプリにする

· unvell team
脱・神Excel ― 方眼紙でつくった帳票を、作り直さずWebアプリにする

日本のオフィスには、どこにでも「方眼紙Excel」があります。セルを正方形に整え、結合セルと罫線でレイアウトを組み、見積書・請求書・申請書・報告書といった帳票をつくる ― いわゆる「神Excel」です。データベースというより、方眼紙そのものとして使われています。

そこへ「これをWebアプリにして」という要件が来ると、多くの開発者はこう考えます ― HTMLテーブルと CSS で見た目を再現し、PDF 生成ライブラリで印刷に対応する。やってみると、rowspan/colspan の沼にはまり、罫線は印刷でズレ、列幅は合わず、数式は自前で実装し直すことになります。そもそも「作り直す」前提が間違っているのかもしれません。

ReoGrid Web は別のアプローチを取ります。帳票をスプレッドシートそのものとして、結合セル・罫線・表示形式を保ったまま、ブラウザ上で読み書きします。この記事では、神ExcelをWeb化する2つの現実的な道 ― 既存の xlsx をそのまま読み込むコードで組み立てる ― を実コードで示します。


なぜ「神Excel」のWeb化は難しいのか

普通のデータグリッド(表形式のデータ表示)と、帳票は別物です。帳票が持っているのはデータではなくレイアウトだからです。

  • 結合セルが縦横に走り、見出しや宛先欄を構成する
  • 罫線の太さ・位置そのものが書式の一部
  • 列幅・行高が固定で、印刷したときの見た目が決まっている
  • セルに数式(小計・消費税・合計)が埋まっている

これを HTML テーブルで「近似」しようとすると、見た目はだいたい合っても、Excel の感触にはなりません。そして決定的なのが、手元にすでにある Excel テンプレートを再利用できない点です。経理や総務が10年使ってきた見積書フォーマットを、ピクセル単位で作り直すことになります。


方針A:既存の帳票 xlsx を、そのまま読み込む

いちばん手っ取り早いのは「作り直さない」ことです。すでにある .xlsx テンプレートを、そのままグリッドへ読み込みます。

import { createReogrid } from '@reogrid/lite';

const { worksheet } = createReogrid('#grid');

// ユーザーが選んだファイルから
const input = document.querySelector<HTMLInputElement>('#file')!;
input.addEventListener('change', async (e) => {
  const file = (e.target as HTMLInputElement).files?.[0];
  if (file) await worksheet.loadFromFile(file);
});

// サーバー上のテンプレートから
await worksheet.loadFromUrl('/templates/quotation.xlsx');

loadFromFile は、結合セル・罫線・表示形式・行/列サイズ・埋め込み画像がすべて適用された後に解決します。つまり、方眼紙でつくった帳票の見た目をそのまま保持したうえで、編集可能なグリッドとして表示できます。HTMLテーブルへの「移植作業」はゼロです。

しかも、すべてはブラウザ内で完結します ― ファイルはユーザーの端末から外に出ません。社内の機密帳票をサーバーへアップロードせずに扱える、というのは実務上とても大きな利点です。

読み込みは無料の Lite ティアで動作します。詳細は XLSX 入出力ドキュメントへ。


方針B:帳票をコードで組み立てる

テンプレートが無い、あるいは画面上で動的に生成したい場合は、コードで帳票を組み立てます。鍵になるのは ReoGrid がデータグリッドではなく、本物のスプレッドシートだという点 ― 結合・罫線・列幅・表示形式・数式がすべて API で扱えます。

下は見積書を一から組み立てる完全な例です。

import { createReogrid, NumberFormat } from '@reogrid/lite';

const { worksheet } = createReogrid('#grid');
const yen = NumberFormat.currency('円', 0, 'suffix'); // 300000 → 300,000円

worksheet.suspendRender(); // まとめて描画(ちらつき防止)

// ── 列幅:方眼紙の代わりに「意味のある列」を定義する ──
worksheet.column('A').width = 48;   // No.
worksheet.column('B').width = 260;  // 品名
worksheet.column('C').width = 72;   // 数量
worksheet.column('D').width = 120;  // 単価
worksheet.column('E').width = 140;  // 金額

// ── タイトル(A1:E1 を結合)──
worksheet.range('A1:E1')
  .merge()
  .setValue('御 見 積 書')
  .setStyle({ bold: true, fontSize: 20, textAlign: 'center', verticalAlign: 'middle', color: '#1e3a5f' });
worksheet.row(0).height = 44;

// ── 宛先・見積情報 ──
worksheet.cell('A3').setValue('株式会社サンプル 御中').setStyle({ bold: true, fontSize: 12 });
worksheet.cell('D3').setValue('見積番号').setStyle({ bold: true, textAlign: 'right' });
worksheet.cell('E3').setValue('Q-2026-0042');
worksheet.cell('D4').setValue('発行日').setStyle({ bold: true, textAlign: 'right' });
worksheet.cell('E4').setValue('2026-06-04');

// ── 明細ヘッダー ──
worksheet.range('A6:E6').setStyle({ bold: true, textAlign: 'center', backgroundColor: '#eef2f7', color: '#1e3a5f' });
worksheet.cell('A6').setValue('No.');
worksheet.cell('B6').setValue('品名');
worksheet.cell('C6').setValue('数量');
worksheet.cell('D6').setValue('単価');
worksheet.cell('E6').setValue('金額');

// ── 明細行 ──
const items = [
  { name: 'Webサイト構築 初期費用', qty: 1,  price: 300000 },
  { name: '保守サポート(月額)',   qty: 12, price: 20000 },
  { name: 'ドメイン・SSL証明書',    qty: 1,  price: 15000 },
];
items.forEach((it, i) => {
  const r = 7 + i;                                  // 行 7, 8, 9(A1 形式)
  worksheet.cell(`A${r}`).setValue(i + 1).setStyle({ textAlign: 'center' });
  worksheet.cell(`B${r}`).setValue(it.name);
  worksheet.cell(`C${r}`).setValue(it.qty).setStyle({ textAlign: 'center' });
  worksheet.cell(`D${r}`).setValue(it.price);
  worksheet.setCellInput(r - 1, 4, `=C${r}*D${r}`); // 金額 = 数量 × 単価
});

// ── 小計・消費税・合計 ──
worksheet.cell('D10').setValue('小計').setStyle({ bold: true, textAlign: 'right' });
worksheet.setCellInput(9, 4, '=E7+E8+E9');
worksheet.cell('D11').setValue('消費税(10%)').setStyle({ bold: true, textAlign: 'right' });
worksheet.setCellInput(10, 4, '=E10*0.1');
worksheet.cell('D12').setValue('合計').setStyle({ bold: true, textAlign: 'right', fontSize: 12 });
worksheet.setCellInput(11, 4, '=E10+E11');

// ── 通貨書式(単価・金額に「円」サフィックス)──
worksheet.range('D7:D9').setFormat(yen);
worksheet.range('E7:E12').setFormat(yen);

// ── 罫線 ──
worksheet.range('A6:E9').border({ style: 'solid', color: '#cbd5e1' });                    // 明細テーブルの内外
worksheet.range('A6:E6').border({ style: 'solid', color: '#1e3a5f', width: 2 }, ['bottom']); // ヘッダー下線
worksheet.range('A12:E12').border({ style: 'solid', color: '#1e3a5f', width: 2 }, ['top']);  // 合計上線

worksheet.resumeRender();

ポイントをいくつか。

  • range('A1:E1').merge() で範囲を1つの論理セルに結合します。値・スタイルは**左上のセル(アンカー)**に乗ります。詳細は 結合セルのドキュメント
  • 数式は = で始まる入力そのものです ― setValue('=C7*D7') でも setCellInput(row, col, '=C7*D7') でも数式として評価されます。setCellInput() の引数は 0 始まりの (行, 列) で、式の中のセル参照は A1 形式(行は 1 始まり)なので、ループ内では行番号のズレに注意。
  • =C7*D7 のような算術式は無料の Lite でも動きますSUM などの組み込み関数は Pro です(小計は =SUM(E7:E9) とも書けます)。
  • NumberFormat.currency('円', 0, 'suffix')300,000円 のような日本式の通貨表記に。詳しくは 表示形式のドキュメント

日本の帳票ならではの書式 ― 和暦・赤字▲

帳票でよく要求されるのが、和暦(令和・平成・昭和)と、赤字のマイナス表示です。どちらも Excel 互換の表示形式コードで表現できます。

// 和暦:令和6年6月4日 のように表示
worksheet.cell('E4').setFormat('ggge"年"m"月"d"日"');

// 赤字▲:マイナスを赤の三角で表示(損益計算書などで定番)
worksheet.range('E7:E12').setFormat('#,##0;[赤]▲#,##0');

実際の動作は 和暦・カラー書式デモで確認できます ― 令和/平成/昭和の元号日付と、[赤]/[Red] による赤字表示を、損益計算書サンプルで触れます。


Excel に戻す ― ラウンドトリップ

日本の業務でほぼ必ず出てくるのが「結局、Excel でちょうだい」です。ブラウザで編集した帳票を、.xlsx として書き出せます。

import { createReogrid } from '@reogrid/pro';

const { worksheet } = createReogrid('#grid', { licenseKey: 'YOUR-LICENSE-KEY' });
// ...帳票を読み込み/編集...

worksheet.saveAsXlsx({
  filename: '見積書_2026-06-04.xlsx',
  sheetName: '見積',
});

loadXlsx → 編集 → saveAsXlsx のラウンドトリップでは、セルの値・数式(Excel がすぐ数値を表示できるようキャッシュ済み)・スタイル・結合セル・罫線・行/列サイズ・条件付き書式がすべて保持されます。読み込んだ神Excelを、見た目を崩さずに書き戻せるということです。※表示形式と埋め込み画像の書き出しは現在未対応です(読み込みは対応)。書き出しは Pro の機能です(保存レシピ)。


HTMLテーブルで「作り直す」のと、何が違うか

HTMLテーブル+CSSで作り直すReoGrid Web で読み込む/組み立てる
結合セルrowspan/colspan を手作業merge() / xlsx の結合をそのまま保持
罫線・列幅CSS で近似(印刷でズレやすい)セル単位で指定/Excel と一致
数式(小計・税・合計)自前で再実装組み込みの数式エンジン
既存 Excel テンプレの再利用不可(一から作り直し)loadFromFile でそのまま
Excel へ書き戻し別ライブラリが必要saveAsXlsx(Pro)
セル編集 UIinput/contenteditable を自作セル編集が標準装備

「Excel の見た目と感触を、Web で再現する」目的なら、グリッドを土台にした方が作る量も保守する量も圧倒的に少なくて済みます


Lite と Pro の境界

操作Lite(無料)Pro
xlsx 読み込み(loadFrom*
結合・罫線・表示形式・列幅
算術式(=C7*D7
組み込み関数(SUM 等)
xlsx 書き出し(saveAsXlsx

つまり、神Excelをブラウザで読んで表示・編集するところまでは無料。Excel へ書き戻すところが Pro の境界線です。


まとめ

「神Excel」「方眼紙Excel」は、データではなくレイアウトを持った帳票です。HTML テーブルで作り直すのではなく、スプレッドシートとして扱うのが近道 ― 既存の .xlsx をそのまま読み込むか(方針A)、コードで組み立てるか(方針B)。結合セル・罫線・円書式・和暦まで、すべてブラウザだけで完結します。

結合・レイアウトのデモ和暦・カラー書式デモを触ってみるか、見積書を組み立てるレシピから始めてください。すべてのデモはデモ一覧から確認できます。

ReoGrid Web を試してみる

React/Vue 向けの Canvas ベース Excel 互換スプレッドシートコンポーネント。 Lite は無料 — npm install 一発で始められます。

関連記事

ニュースレター

開発の最新情報をお届けします

新しいリリース・機能追加・お知らせをいち早く受け取るには、
メーリングリストにご登録ください。