react-dnd vs react-draggable vs react-zoom-pan-pinch
Reactアプリケーションにおけるドラッグ&ドロップおよびズーム操作ライブラリの選定
react-dndreact-draggablereact-zoom-pan-pinch類似パッケージ:

Reactアプリケーションにおけるドラッグ&ドロップおよびズーム操作ライブラリの選定

react-dndreact-draggablereact-zoom-pan-pinch はすべてReactアプリケーションでインタラクティブなユーザー操作を実現するためのライブラリですが、それぞれ解決する問題が異なります。react-dndはリスト項目の再配置やウィジェットの配置など、コンポーネント間でのデータ転送を伴うドラッグ&ドロップ操作に特化しています。react-draggableは単一の要素を自由にドラッグ移動させるシンプルな機能を提供します。一方、react-zoom-pan-pinchは画像やキャンバス全体に対するズーム、パン(平行移動)、ピンチ操作を実現し、インタラクティブなビューア系UIに最適です。これらは目的が異なるため、直接的な代替関係ではなく、要件に応じて適切に使い分ける必要があります。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
react-dnd021,637231 kB474-MIT
react-draggable09,290243 kB2149ヶ月前MIT
react-zoom-pan-pinch01,851441 kB1591年前MIT

Reactでのドラッグ&ドロップとズーム操作ライブラリの比較:react-dnd vs react-draggable vs react-zoom-pan-pinch

ReactアプリケーションでインタラクティブなUIを実装する際、要素のドラッグ操作やキャンバスのズーム・パン機能はよく必要になります。react-dndreact-draggablereact-zoom-pan-pinch はそれぞれ異なる目的とアプローチでこれらの要件に対応します。この記事では、各ライブラリの設計思想、技術的特徴、適切な使用シーンをコード例とともに詳しく比較します。

🧩 コア目的と設計思想

react-dnd は「ドラッグアンドドロップ」に特化した高レベルな抽象化を提供します。HTML5 Drag and Drop API をラップし、コンポーネント間のデータ転送(例:リスト項目の再順序付け、ツールボックスからのウィジェット配置)を宣言的に扱えるように設計されています。内部でReact Contextとカスタムフックを使用し、複雑な状態管理を隠蔽しています。

react-draggable は単一要素の自由ドラッグ(位置移動)に焦点を当てたシンプルなライブラリです。要素をマウスやタッチで任意の場所にドラッグできる機能を提供し、制限領域やハンドル指定などの基本的なオプションを備えています。DnDとは異なり、他の要素との「ドロップ」インタラクションはサポートしていません。

react-zoom-pan-pinch は、画像やSVG、キャンバス全体に対する「ズーム」「パン(平行移動)」「ピンチ操作」を実現するためのライブラリです。モバイル対応のマルチタッチ操作やホイールスクロールによるズームを含み、インタラクティブなビューアや地図UIに最適です。ドラッグ操作はあくまでパン(ビューの移動)の一部として扱われます。

🖱️ 基本的なドラッグ操作の実装方法

react-dnd: ドラッグ可能なアイテムとドロップゾーンの分離

react-dnd では、useDraguseDrop フックを別々のコンポーネントで使用し、データをやり取りします。

// アイテム側(ドラッグ可能)
import { useDrag } from 'react-dnd';

function DraggableItem({ id, text }) {
  const [{ isDragging }, drag] = useDrag(() => ({
    type: 'ITEM',
    item: { id, text },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging(),
    }),
  }));

  return (
    <div ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }}>
      {text}
    </div>
  );
}

// ドロップゾーン側
import { useDrop } from 'react-dnd';

function DropZone({ onDrop }) {
  const [{ isOver }, drop] = useDrop(() => ({
    accept: 'ITEM',
    drop: (item) => onDrop(item),
    collect: (monitor) => ({
      isOver: !!monitor.isOver(),
    }),
  }));

  return (
    <div ref={drop} style={{ backgroundColor: isOver ? '#f0f0f0' : '#fff' }}>
      ドロップしてください
    </div>
  );
}

react-draggable: 単一要素の自由ドラッグ

react-draggable は、コンポーネントを直接ラップするだけでドラッグ可能になります。

import Draggable from 'react-draggable';

function DraggableBox() {
  return (
    <Draggable>
      <div style={{ width: 100, height: 100, background: 'lightblue' }}>
        ドラッグ可能
      </div>
    </Draggable>
  );
}

// 制限付きドラッグ(親要素内のみ)
function ConstrainedDrag() {
  return (
    <div style={{ position: 'relative', height: 300, border: '1px solid #ccc' }}>
      <Draggable bounds="parent">
        <div>親の中だけ動く</div>
      </Draggable>
    </div>
  );
}

react-zoom-pan-pinch: キャンバス全体のパン操作

このライブラリでは、ドラッグは「パン」として扱われ、特定の要素ではなく全体のビューを移動します。

import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';

function ZoomableImage() {
  return (
    <TransformWrapper>
      {({ zoomIn, zoomOut }) => (
        <>
          <button onClick={() => zoomIn()}>+</button>
          <button onClick={() => zoomOut()}>-</button>
          <TransformComponent>
            <img src="/map.jpg" alt="Zoomable" />
          </TransformComponent>
        </>
      )}
    </TransformWrapper>
  );
}

🔗 データ転送 vs 物理移動 vs ビュー操作

各ライブラリが解決する問題は根本的に異なります。

  • react-dnd は「データの移動」を扱います。UI要素の見た目の位置変更ではなく、背後にあるデータ(例:配列の並び替え)を更新することを目的とします。実際のDOM位置はCSSやFlexboxなどで制御されます。

  • react-draggable は「要素の物理的位置変更」を扱います。要素のtransform: translate(x, y)を直接操作し、画面上の絶対位置を変更します。データモデルとの同期は開発者自身で行う必要があります。

  • react-zoom-pan-pinch は「ビューポートの操作」を扱います。コンテンツ自体は固定され、ユーザーがズームやパンによって表示範囲を変更できるようにします。これはPDFビューアや地図アプリのようなシナリオに適しています。

⚙️ 設定とカスタマイズ性

イベントハンドリング

react-dnd は、ドラッグ開始・終了・ドロップなどのライフサイクルイベントをフック内で処理します。

const [{}, drag] = useDrag(() => ({
  // ...
  end: (item, monitor) => {
    if (!monitor.didDrop()) {
      // ドロップされなかった場合の処理
    }
  }
}));

react-draggable は、onStartonDragonStop といったコールバックをpropsとして受け取ります。

<Draggable
  onStop={(e, data) => {
    console.log('最終位置:', data.x, data.y);
  }}
>
  <div>...</div>
</Draggable>

react-zoom-pan-pinch は、onZoomonPanonPinch などのイベントを提供し、現在の変換状態を取得できます。

<TransformWrapper
  onZoom={(ref) => console.log('ズームレベル:', ref.state.scale)}
>
  {/* ... */}
</TransformWrapper>

制約と境界

  • react-draggablebounds prop でドラッグ範囲を制限できます(例:bounds="parent"{ left: 0, top: 0, right: 100, bottom: 100 })。
  • react-dnd は視覚的な制約よりも、論理的な制約(どのタイプのアイテムを受け入れるか)に重点を置きます。
  • react-zoom-pan-pinchminScale/maxScalelimitToBounds でズームとパンの範囲を制御できます。

📱 モバイルとタッチデバイス対応

  • react-dnd は、標準のHTML5 DnD APIに依存しているため、iOS Safariなど一部のモバイルブラウザで動作しないことがあります。公式ドキュメントでは、モバイル対応が必要な場合は代替手段を検討するよう推奨しています。
  • react-draggable はタッチイベントをネイティブにサポートしており、スマートフォンやタブレットでも問題なく動作します。
  • react-zoom-pan-pinch はピンチ操作を含むマルチタッチジェスチャーを完全にサポートしており、モバイルファーストのUIに最適です。

🛠 適切な使用シーンの判断基準

react-dnd を選ぶべきケース

  • リストの並べ替え(ToDoリスト、Kanbanボード)
  • ツールボックスからキャンバスへのウィジェット配置
  • ファイルエクスプローラー風のドラッグ&ドロップ
  • データモデルの再構成が主目的で、UIは副次的

react-draggable を選ぶべきケース

  • ダイアログやポップアップの自由移動
  • ゲーム内のキャラクターやオブジェクトのドラッグ
  • カスタムスライダーやハンドルの実装
  • 単一要素の位置制御が主目的

react-zoom-pan-pinch を選ぶべきケース

  • 画像ビューア(地図、図面、写真)
  • SVGやCanvasベースのインタラクティブな図表
  • PDFビューアのズーム・パン機能
  • マルチタッチ対応が必須のシナリオ

🔄 組み合わせの可能性

これらのライブラリは互いに排他的ではありません。例えば、react-zoom-pan-pinch で全体をズーム可能なキャンバスを作り、その中に react-draggable で配置可能なノードを配置するような複合UIも可能です。ただし、イベントの競合(例:パン操作と要素ドラッグの衝突)には注意が必要です。

💡 まとめ

特徴react-dndreact-draggablereact-zoom-pan-pinch
主目的データ転送(DnD)要素の物理移動ビューのズーム・パン
モバイル対応限定的完全対応完全対応(ピンチ含む)
データバインディング内蔵(Context経由)手動変換状態の取得可
複雑さ高(抽象化が多い)低(シンプル)中(ビュー変換の理解が必要)
典型ユースケースKanbanボードモーダルウィンドウ画像ビューア

結論として、「何をドラッグしたいのか?」 という問いが選択の鍵になります。

  • 「データを別の場所に移したい」react-dnd
  • 「このボックスを好きな場所に動かしたい」react-draggable
  • 「この画像を拡大縮小して見たい」react-zoom-pan-pinch

目的に合ったライブラリを選ぶことで、不要な複雑さを避け、メンテナンス性の高いコードを書くことができます。

選び方: react-dnd vs react-draggable vs react-zoom-pan-pinch

  • react-dnd:

    react-dndは、Kanbanボードやファイルエクスプローラーのように、要素をドラッグして別のコンテナにドロップし、背後のデータを更新する必要がある複雑なドラッグ&ドロップUIに適しています。HTML5 DnD APIの制限(特にモバイルブラウザでの非対応)を許容できる場合に選んでください。単純な要素移動だけが必要なら、オーバーヘッドが大きすぎます。

  • react-draggable:

    react-draggableは、モーダルウィンドウやゲーム内のオブジェクトなど、単一要素を自由に画面上で移動させたいシンプルなケースに最適です。タッチデバイスを含む幅広い環境で動作し、設定も直感的です。ただし、他の要素とのドロップインタラクションやデータ転送はサポートしていない点に注意してください。

  • react-zoom-pan-pinch:

    react-zoom-pan-pinchは、地図、図面、写真ビューアなど、コンテンツ全体をユーザーがズーム・パン操作できるインタラクティブなビューワーを実装する必要がある場合に選んでください。マルチタッチピンチ操作やホイールズームをネイティブにサポートしており、モバイルファーストのアプリケーションに非常に適しています。

react-dnd のREADME

npm version npm downloads Build Status

React DnD

Drag and Drop for React.

See the docs, tutorials and examples on the website:

http://react-dnd.github.io/react-dnd/

See the changelog on the Releases page:

https://github.com/react-dnd/react-dnd/releases

Big thanks to BrowserStack for letting the maintainers use their service to debug browser issues.