はじめに
「この在庫表、Excelで管理してるんだけど、Webシステムに移行できない?ただし操作感はExcelのままで」——業務システムの開発をしていると、一度は突きつけられる要望ではないでしょうか。
普通のHTMLテーブルとフォームで作り直すと、「セルをコピペできない」「矢印キーで移動できない」「使いにくくなった」と現場から不満が噴出します。Excelに慣れきったユーザーの期待値は、想像以上に高いのです。
そんなときに頼りになるのがHandsontableです。セルのコピー&ペースト、キーボード操作、数式、フィルタリングまで、Excelそっくりの編集体験をそのままWebアプリに埋め込めるデータグリッドライブラリです。この記事では、基本の導入から実践的な使い方までを解説します。
とはいえ、説明を読むより実際にセルを編集してみた方が早いと思います。ブラウザ上でそのまま値を書き換えられるHandsontableのサンプルを用意したので、先に動きを見たい方はこちらからどうぞ。
Handsontableとは
Handsontableは、スプレッドシートのようなUIと操作性を提供するJavaScript製のデータグリッドコンポーネントです。2012年から開発が続く老舗ライブラリで、2026年6月にはバージョン18.0.0がリリースされるなど、現在も活発にメンテナンスされています。
素のJavaScriptはもちろん、React・Angular・Vue 3向けの公式ラッパーが用意されているため、モダンなフロントエンド開発にもそのまま組み込めます。
主な特徴
- Excelライクな操作性 - セル編集、コピー&ペースト、フィルハンドルによるオートフィル、キーボードナビゲーションなど、スプレッドシートの操作感をほぼ再現しています
- 400以上の組み込み数式 - 数式エンジンのHyperFormulaと連携することで、
SUMやVLOOKUPといったExcel互換の数式をセルに書けます - 仮想化による高パフォーマンス - 行・列の仮想レンダリングにより、大量データでもスムーズにスクロールできます
- 豊富なデータ操作機能 - ソート、フィルタリング、データバリデーション、条件付き書式、セル結合、行列の固定・非表示などを標準サポートしています
- フレームワーク対応 - React、Angular、Vue 3の公式ラッパーが提供されています
ライセンスについて(重要)
導入前に必ず押さえておきたいのがライセンスです。Handsontableは商用利用には有料ライセンスが必要です。非商用利用や評価目的であれば、ライセンスキーに non-commercial-and-evaluation を指定することで無料で使えます。業務システムに組み込む場合はライセンス費用を見込んでおきましょう。
インストール
npmで一発です。素のJavaScriptで使う場合は本体のみをインストールします。
# npm
npm install handsontable
# yarn
yarn add handsontable
# pnpm
pnpm add handsontable
Reactで使う場合は公式ラッパーも一緒に入れます(React 18以上が必要です)。
npm install handsontable @handsontable/react-wrapper
Handsontableのサンプルを動かす
まずは実際に手を動かして、Handsontableの編集感覚をつかんでみましょう。下のグリッドはセルをダブルクリックまたは矢印キーで選択してそのまま入力すると編集でき、Excelと同様に範囲選択やコピー&ペーストにも対応しています。値を書き換えるたびにafterChangeフックが発火し、変更内容が下の履歴欄にリアルタイムで追記されます。要点だけを抜き出すと、次のようなコードになります。
import Handsontable from 'handsontable'
new Handsontable(container, {
data: [
['りんご', 150, 120],
['みかん', 80, 300],
],
colHeaders: ['商品名', '単価', '在庫数'],
rowHeaders: true,
theme: 'ht-theme-main',
afterChange(changes, source) {
if (source === 'loadData' || !changes) return
changes.forEach(([row, prop, oldValue, newValue]) => {
console.log(`${row}行目 ${prop}: ${oldValue} → ${newValue}`)
})
},
licenseKey: 'non-commercial-and-evaluation',
})
実際に動かせるものが下です。handsontable@18.0.0をCDN経由で読み込み、スタイル用CSSも動的に差し込んでいます。
「在庫数」列のセルを120から0に書き換えてみてください。afterChangeの第2引数changesには[行番号, 列名, 変更前の値, 変更後の値]という配列が渡ってくるので、それをそのまま履歴欄に反映しています。実務ではこのフックの中でAPIを呼び出せば、セル編集のたびにサーバー側へ自動保存する仕組みが作れます。theme: 'ht-theme-main'の行を消すと見た目がどう変わるかも、あわせて試してみてください。
基本的な使い方
まずは素のJavaScriptでの最小構成です。コンテナ要素を用意して、データを渡すだけで動きます。
<div id="grid"></div>
import Handsontable from 'handsontable';
import 'handsontable/styles/handsontable.min.css';
import 'handsontable/styles/ht-theme-main.min.css';
const container = document.getElementById('grid');
new Handsontable(container, {
data: [
['商品名', '単価', '在庫数'],
['りんご', 150, 120],
['みかん', 80, 300],
['バナナ', 100, 85],
],
rowHeaders: true,
colHeaders: true,
height: 'auto',
licenseKey: 'non-commercial-and-evaluation',
});
これだけで、セルをダブルクリックして編集したり、範囲選択してコピー&ペーストしたりできる表が表示されます。Excelからセル範囲をコピーして、そのまま貼り付けることも可能です。この「Excelとの相互コピペ」が現場で驚くほど喜ばれます。
Reactでの使い方
Reactでは HotTable コンポーネントを使います。設定オプションはすべてpropsとして渡します。
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import 'handsontable/styles/handsontable.min.css';
import 'handsontable/styles/ht-theme-main.min.css';
// すべてのモジュールを一括登録
registerAllModules();
export default function InventoryGrid() {
return (
<HotTable
data={[
['りんご', 150, 120],
['みかん', 80, 300],
['バナナ', 100, 85],
]}
colHeaders={['商品名', '単価', '在庫数']}
rowHeaders={true}
height="auto"
licenseKey="non-commercial-and-evaluation"
/>
);
}
registerAllModules() は全機能を一括で有効化する関数です。バンドルサイズを絞りたい場合は、必要なモジュールだけを個別に登録することもできます。
実践的なユースケース
ここからは、業務システムでよくある「在庫管理表」を想定した実践例です。列ごとの型定義、バリデーション、ドロップダウン、変更検知を組み合わせます。
import { useRef } from 'react';
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import 'handsontable/styles/handsontable.min.css';
import 'handsontable/styles/ht-theme-main.min.css';
registerAllModules();
const initialData = [
{ name: 'りんご', category: '果物', price: 150, stock: 120 },
{ name: 'みかん', category: '果物', price: 80, stock: 300 },
{ name: 'キャベツ', category: '野菜', price: 200, stock: 45 },
];
export default function InventoryManager() {
const hotRef = useRef(null);
return (
<HotTable
ref={hotRef}
data={initialData}
columns={[
{ data: 'name', title: '商品名' },
{
data: 'category',
title: 'カテゴリ',
type: 'dropdown',
source: ['果物', '野菜', '飲料'],
},
{
data: 'price',
title: '単価',
type: 'numeric',
numericFormat: { pattern: '0,0', culture: 'ja-JP' },
},
{
data: 'stock',
title: '在庫数',
type: 'numeric',
validator: (value, callback) => {
// 在庫数は0以上の整数のみ許可
callback(Number.isInteger(value) && value >= 0);
},
},
]}
rowHeaders={true}
columnSorting={true}
filters={true}
dropdownMenu={true}
afterChange={(changes, source) => {
if (source === 'loadData' || !changes) return;
// 変更されたセルの情報をログ出力(実際はここでAPIに保存)
changes.forEach(([row, prop, oldValue, newValue]) => {
console.log(`${row}行目の${prop}: ${oldValue} → ${newValue}`);
});
}}
height="auto"
licenseKey="non-commercial-and-evaluation"
/>
);
}
このサンプルのポイントを整理します。
type: 'dropdown'- カテゴリ列はドロップダウンから選択させることで、表記ゆれを防ぎますvalidator- 在庫数に負の数や小数が入力されると、セルが赤くハイライトされて弾かれますcolumnSorting/filters/dropdownMenu- 列ヘッダーからソートとフィルタリングができるようになり、Excelのオートフィルタと同じ感覚で使えますafterChange- セルの変更を検知するフックです。ここでAPIを呼べば、編集内容の自動保存が実現できます
Handsontableのドロップダウン・バリデーション・フィルタを試す
上記のReact実装で使っているtype: 'dropdown'・validator・filters・dropdownMenu・columnSortingは、素のJavaScript版でも同じオプション名で使えます。要点だけを抜き出すと、次のようになります。
columns: [
{ data: 'category', type: 'dropdown', source: ['果物', '野菜', '飲料'] },
{
data: 'stock',
type: 'numeric',
validator(value, callback) {
callback(Number.isInteger(value) && value >= 0)
},
},
],
columnSorting: true,
filters: true,
dropdownMenu: true,
実際に動かせるものが下です。「カテゴリ」列はドロップダウンから選択、「在庫数」列は0以上の整数しか入力できません。列見出しの右端にあるフィルタアイコンからは、Excelのオートフィルタと同じ感覚で絞り込みができます。
在庫数セルに-5のような負の数や小数を入力すると、validatorがfalseを返してセルが赤くハイライトされ、下のステータス欄にNGが表示されます。カテゴリ列をクリックするとsourceで指定した3択のドロップダウンが開き、表記ゆれを防げます。列ヘッダーのソートアイコンやフィルタアイコンも合わせて試してみてください。
数式を使いたい場合
Excel互換の数式が必要なら、同じ開発元が提供する数式エンジンHyperFormulaを組み合わせます。
npm install hyperformula
import { HyperFormula } from 'hyperformula';
new Handsontable(container, {
data: [
['単価', '数量', '小計'],
[150, 3, '=A2*B2'],
[80, 10, '=A3*B3'],
['', '合計', '=SUM(C2:C3)'],
],
formulas: {
engine: HyperFormula,
},
licenseKey: 'non-commercial-and-evaluation',
});
セルに =SUM(C2:C3) のような数式を書くと自動計算され、参照元のセルを編集すれば結果も即座に更新されます。「Excelの数式ごと移行したい」という要望にも応えられるわけです。
Handsontableの数式サンプルを動かす
実際にHyperFormulaと組み合わせたグリッドを動かしてみましょう。formulas.engineにHyperFormulaを渡すだけで、Handsontableのセルに書いた数式が自動計算されるようになります。
A2セルの単価やB2セルの数量を書き換えると、=A2*B2が入ったC2セルと、=SUM(C2:C3)が入ったC4セル(合計)の両方が連動して再計算されます。C2セルの数式を=A2*B2*1.1のように書き換えれば、消費税込みの小計に変えるといった応用もその場で確認できます。
まとめ
Handsontableについて、基本から実践的な使い方までを紹介しました。
- Excelライクな操作性(コピペ・キーボード操作・オートフィル)をWebアプリにそのまま持ち込めます
- ドロップダウン、バリデーション、ソート、フィルタなど業務システムに必要な機能が標準で揃っています
- HyperFormulaと組み合わせれば、400以上のExcel互換数式まで使えます
- 商用利用は有料ライセンスが必要な点だけは、導入前に必ず確認しましょう
「Excelで回っている業務をWebに移行したい。でも操作感は落としたくない」——そんな案件を抱えているなら、Handsontableは最有力の選択肢です。まずは非商用ライセンスキーで手元に動くプロトタイプを作り、現場の反応を見てから商用ライセンスを検討する、という進め方がおすすめです。
