1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

業務で欲しいnpmライブラリがなかったので、React向けライブラリを作ってnpmに公開してみた

1
Posted at

はじめに

こんにちは、世界、k-girigiriです。

最近、業務で温度分布を可視化する処理を実装していたとき、

「この処理、汎用的なライブラリがあればいいのに」

と思うことがありました。

調べてみたものの、自分が求めている形のライブラリが見つからなかったので、

「それなら勉強も兼ねて自分で作ってみよう」

と思い、React向けのnpmライブラリを作ることにしました。

そして今回、無事にnpmへ公開するところまでやり切りました。

実際作成したライブラリ:scalar-field-react - npm

この記事では、npmパッケージを作ったことがほとんどない人向けに、

  • npmライブラリをどうやって作ったか
  • 普通のReactアプリと何が違うのか
  • package.jsonをどう設定したか
  • 公開前に何を確認したか
  • 実際にnpm publishするとどうなるのか

といったところを、実際に作った過程に沿ってまとめていきます。

今回作ったもの

今回作ったのは、scalar-field-react というReact向けのCanvas可視化ライブラリです。

ざっくり言うと、

座標(x, y)と数値(value)を持ったデータから、2次元の数値分布を補間してCanvasに可視化するライブラリ

です。

例えば、複数の地点で温度を測定したとして、

30℃                 40℃


       35℃


20℃                 45℃

のように「測定した地点の温度」しか分からない場合でも、各地点の値をもとにその間の値を補間することで、エリア全体の温度分布を色のグラデーションとして表現することができます。

今回のライブラリでは、この補間にGaussian interpolation(周囲のデータ点に近いほど強く影響させる補間方法)を利用しています。

温度に限らず、(x, y, value) という形で表現できるデータであれば利用できるようにしています。

例えば、

  • 温度
  • 湿度
  • 電波強度
  • センサー値

などの2次元分布の可視化を想定しています。

実際には、次のようなデータを渡します。

const data: DataPoint[] = [
  { x: 10, y: 10, value: 22 },
  { x: 80, y: 20, value: 31 },
  { x: 50, y: 70, value: 27 },
  { x: 20, y: 90, value: 19 },
];

そしてReactから、

function App() {
  return (
    <div className="floor-map">
      {/* 背景: この画像の表示サイズがコンテナの大きさになる */}
      <img className="floor-image" src="/floor.jpg" alt="Floor plan" />

      {/* 画像と同じ領域に絶対配置し、responsive で親サイズに追従 */}
      <div className="overlay">
        <ScalarFieldCanvas
          data={data}
          bounds={{ minX: 0, maxX: 100, minY: 0, maxY: 100 }}
          grid={{ width: 200, height: 200 }}
          colorMap="jet"
          min={15}
          max={35}
          opacity={0.55}
          responsive
        />
      </div>
    </div>
  )
}

のように利用できます。

上記実装で実際の表示がこちら

【室内温度分布図Demo】
スクリーンショット 2026-08-26 21.48.36.png

主な機能

今回のライブラリでは、主に以下を実装しました。

  • Gaussian interpolation
  • sigma の指定
  • 最近傍点数 neighbors の指定
  • Polygonによる描画範囲のマスク
  • min / max によるColor Mapping範囲指定
  • opacity
  • Matplotlibの jet 相当のColorMap
  • Custom ColorMap
  • Gridサイズ指定
  • Canvasのレスポンシブ対応
  • Retina / DPR対応
  • SSR環境での安全なimport
  • TypeScript型定義
  • ESM
  • Tree-shaking対応

…と、最初は「ちょっとした可視化処理」のつもりだったのですが、ライブラリとして使える形を考えていくと、思ったよりちゃんとした構成になりました。

そもそも、なぜnpmライブラリにしたのか

今回のスタート地点は、「npmライブラリを作ってみたい」ではありませんでした。

業務で必要になった処理を実装していて、

「これ、今後も使いそうだな」

と思ったのがきっかけです。

最初は既存のライブラリを探しました。

しかし、

  • Reactに依存しすぎている
  • 自分が必要としているデータ形式と合わない
  • 補間方式を細かく指定できない
  • Canvas描画まで含めて自由に扱えない

など、ちょうどいいものが見つかりませんでした。

そこで、

「ないなら、自分で作ってみるか」

となりました。

せっかく作るなら、単に業務コードをコピーするのではなく、

「別のReactプロジェクトからnpm installして使えるライブラリ」

として設計してみることにしました。

npmライブラリってどうやって作るの?

ここからが今回の記事の本題です。

npmライブラリを作った経験がなかったので、最初は、

「普通にTypeScriptを書いてnpm publishすればいいのかな?」

くらいに思っていました。

実際には、

ソースコード
   ↓
ライブラリとして設計
   ↓
TypeScript / Build
   ↓
package.json
   ↓
npm package
   ↓
npm pack
   ↓
npm publish

という流れになります。

特に重要だったのが、

「自分の開発環境で動く」ことと「他の人がnpm installして使える」ことは別

という点でした。

まずはGitHubリポジトリを作る

今回はGitHubに、

scalar-field-react

というリポジトリを作りました。

GitHubリポジトリ

ライブラリとして設計する

今回、普通のReactアプリとは違うところを意識しました。

例えば、アプリケーションなら、

src/
 ├─ components/
 ├─ pages/
 └─ hooks/

のような構成でも問題ありません。

しかしライブラリの場合は、

「利用者に何を公開するのか」

を最初から考える必要があります。

今回のPublic APIは、

import { ScalarFieldCanvas } from 'scalar-field-react';

を入口にしています。

また、Reactコンポーネントだけではなく、必要に応じてheadlessな処理も利用できるようにしました。

import {
  buildScalarGrid,
  renderScalarGrid,
} from 'scalar-field-react';

こうすることで、

ReactでCanvasに描画したい人

だけでなく、

補間やRasterizationの処理だけ利用したい人

にも使える構成にしています。

Reactだけに依存しない設計

今回かなり意識したのが、コア処理とReactを分離することでした。

ざっくり、

DataPoint
    ↓
Grid生成
    ↓
Gaussian interpolation
    ↓
Color Mapping
    ↓
Raster
    ↓
Canvas

という処理の流れになっています。

このうち、

  • Geometry
  • Gaussian interpolation
  • Polygon
  • ColorMap
  • Rasterization

などはReactやブラウザAPIに依存しないようにしました。

そのため、コア部分は純粋関数中心でテストできます。

一方でReact側には、

ScalarFieldCanvas
ResizeObserver
Canvas
DPR
React lifecycle

などを閉じ込めています。

この分離は、今回ライブラリを作るうえでかなり勉強になりました。

package.jsonを設定する

npmパッケージとして公開するためには、package.jsonも重要です。

今回の主要な設定はこんな感じです。

{
  "name": "scalar-field-react",
  "version": "0.1.0",
  "type": "module",
  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "sideEffects": false,
  "peerDependencies": {
    "react": ">=18.0.0"
  }
}

ここで初めて、

「あ、アプリケーションのpackage.jsonとはちょっと違うな」

となりました。

peerDependencies

Reactはライブラリ自身が抱え込むのではなく、

"peerDependencies": {
  "react": ">=18.0.0"
}

としました。

これは、

「このライブラリを使う側のReactを使ってください」

という考え方です。

ライブラリ側にReactを重複してインストールしてしまうのを避けています。

TypeScriptの型定義

TypeScriptライブラリなので、

"types": "./dist/index.d.ts"

も設定しました。

Buildすると、

dist/
 ├─ index.js
 ├─ index.js.map
 └─ index.d.ts

が生成されます。

📸 【スクショ③:distの生成結果】

npm run build

> scalar-field-react@0.1.0 build
> tsup

CLI Building entry: src/index.ts
CLI Using tsconfig: tsconfig.json
CLI tsup v8.5.1
CLI Using tsup config: /Users/kawagiri/dev/scalar-field-react/tsup.config.ts
CLI Target: es2020
CLI Cleaning output folder
ESM Build start
ESM dist/index.js     19.78 KB
ESM dist/index.js.map 56.90 KB
ESM ⚡️ Build success in 57ms
DTS Build start
DTS ⚡️ Build success in 369ms
DTS dist/index.d.ts 11.82 KB

ESMとして公開する

今回のライブラリはESMで公開しています。

"type": "module"

に加えて、

"exports": {
  ".": {
    "types": "./dist/index.d.ts",
    "import": "./dist/index.js"
  },
  "./package.json": "./package.json"
}

としています。

このあたりは、npmライブラリを作るまであまり意識していなかった部分でした。

npmに公開する前にテストする

いきなり、

npm publish

するのは怖いので、まずパッケージとして何が入るのか確認します。

そこで使ったのが、

npm pack --dry-run

です。

今回の結果では、

npm notice Tarball Contents
npm notice 1.1kB LICENSE
npm notice 10.6kB README.md
npm notice 12.1kB dist/index.d.ts
npm notice 20.3kB dist/index.js
npm notice 58.3kB dist/index.js.map
npm notice 2.1kB package.json

npm notice total files: 6

となりました。

【npm pack --dry-runの結果】

スクリーンショット 2026-08-25 21.39.42.png

Tarballから実際にインストールしてみる

さらに一歩進めて、生成されたnpmパッケージそのものを使って確認しました。

scalar-field-react
        ↓
npm pack
        ↓
scalar-field-react-0.1.0.tgz
        ↓
別のconsumer環境
        ↓
npm install ./scalar-field-react-0.1.0.tgz
        ↓
import

これをテストするスクリプトも用意しました。

これによって、

「リポジトリでは動くけど、npmに入れたら動かない」

という問題を公開前に確認できます。

いよいよnpm publish

そしていよいよ、

npm publish

です。

今回、最初はここで少しだけハマりました。

まさかの403

最初に npm publish したところ、

npm error code E403
npm error 403 Forbidden

となりました。

原因はnpmアカウントの2要素認証でした。

npm側で2FAを設定していなかったため、公開が拒否されました。

そこでnpmのアカウント設定からSecurity keyを登録しました。

【npmのSecurity key登録画面】
スクリーンショット 2026-08-26 22.09.35.png

そして、初めてのnpm publish

2FA設定後、もう一度、

npm publish

を実行。

すると、

Publishing to https://registry.npmjs.org/

と進み、最後に、

+ scalar-field-react@0.1.0

と表示されました。

公開成功です。

この瞬間が個人開発で一番脳汁出るので、手動コマンドで実施を推奨します。笑

【npm publish成功画面】

スクリーンショット 2026-08-26 22.12.34.png


npm上で確認してみる

公開後、npmのページを確認します。

scalar-field-react - npm

そこには、

scalar-field-react
0.1.0

として公開されています。

【npmページ】
スクリーンショット 2026-08-25 21.47.32.png

ここまで来ると、

「本当にnpmに公開されたんだな」

と実感します。

実際にnpm installしてみる

最後に、別の環境からインストールします。

npm install scalar-field-react

そして、

import { ScalarFieldCanvas } from 'scalar-field-react';

として利用します。

これで、

自分のPC
 ↓
GitHub
 ↓
npm Registry
 ↓
npm install
 ↓
別のReactアプリ

という一連の流れが成立しました。

作ってみて分かったこと

今回、初めてnpmライブラリを作ってみて一番感じたのは、

「ライブラリを作ること」と「アプリを作ることは結構違う」

ということでした。

アプリの場合は、

自分の環境で動けばOK

となりがちですが、ライブラリの場合は、

他の人の環境でも自然に使えるか

を考える必要があり、これは少し緊張感がありました。

そのため、

  • Public API
  • TypeScriptの型
  • ESM
  • package.json
  • peerDependencies
  • tree-shaking
  • npm packageの中身
  • README
  • CI
  • テスト
  • SSR
  • npm publish

など、普段のアプリ開発とは違った観点をたくさん学ぶことができました。

まとめ

今回は、業務で必要になった処理をきっかけに、React向けのnpmライブラリを個人開発してみました。

最初は、

「欲しいライブラリがないなら作ってみよう」

くらいの気持ちでしたが、最終的にはnpmに公開するところまでやり切ることができました。

今回作ったライブラリはこちらです。

GitHub - scalar-field-react

scalar-field-react - npm

npmパッケージを作ったことがない方でも、この記事が

「意外とnpmライブラリって作って公開できるんだな」

と思うきっかけになれば嬉しいです。

自分自身も今回初めてnpmパッケージを作りましたが、実際に npm publish が成功して、自分の作ったライブラリがnpmに並んだ瞬間はかなり嬉しかったです。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?