2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【dnd-kit】Reactでのドラッグ&ドロップライブラリdnd-kitの基本的な使い方

2
Last updated at Posted at 2025-08-28

0.はじめに

Gemini-CLIでTrelloクローンアプリを作成した際に、
カンバンのドラッグ&ドロップ機能で、
dnd-kitを採用しました。
実装自体をGemini-CLIにまかせてしまったので、
内容を理解するために、記事を作成することにしました。

1.dnd-kitとは

Reactアプリケーション向けのモダンで軽量なドラッグ&ドロップライブラリ。
カンバンボードのような相互作用のあるUIを簡単に実装することが可能。

1.1 主な特徴

【軽量性】

  • コアライブラリは約10KBと非常に軽量
    コア部分は非常に小さく、約10KBの少量サイズ。
    そのため、アプリケーションのバンドルサイズを最小限に抑えることができ、
    ページ読み込み速度の向上が可能となる。

  • 外部依存がない自己完結型の設計
    外部ライブラリに依存せず、自己完結型で設計されているため、
    他のライブラリとの競合を避け、安定した動作を保証。

【React最適化】

  • React Hooksベースの直感的なAPI
    ReactのHooksを活用し、
    useDraggableやuseDroppableなどのフックを提供
    Reactコンポーネント内でドラッグ&ドロップ機能を簡単に実装可能となっている。

  • TypeScript完全対応
    TypeScriptを使用することで、型安全性を確保しつつ、
    開発者が効率的にコーディングできるようサポート。
    dnd-kitはTypeScriptでの使用を前提に設計されており、
    型定義が豊富に用意されている。

【豊富な機能】

  • リスト、グリッド、複数コンテナ対応
    リストやグリッド、複数のコンテナ間でのドラッグ&ドロップをサポート。
    複雑なUIレイアウトでも柔軟に対応可能。

  • ネストされたドラッグ&ドロップ
    ネストされた構造(例えば、リスト内のリスト)でも
    直感的な操作でドラッグ&ドロップが可能。

【高いカスタマイズ性】

  • アニメーション・トランジション制御
    ドラッグ中やドロップ後のアニメーションをカスタマイズすることができ、
    よりスムーズで視覚的に魅力的なユーザー体験を提供。

2.プロジェクト作成・インストール

※本記事では下記3つのみとなりますが、
@dnd-kit/modifiersなどのライブラリもあります。

# Next.jsプロジェクト作成
npx create-next-app@latest dnd-kit-example
cd dnd-kit-example

# dnd-kitライブラリ一式インストール
npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities

@dnd-kit/core

ドラッグ&ドロップ機能のコアライブラリ

@dnd-kit/sortable

並び替え(Sortable)機能を提供するためのライブラリ

@dnd-kit/utilities

ドラッグ&ドロップの実装を助けるユーティリティ関数が含まれたライブラリ。

3.サンプルコンポーネント作成

3.1 カード作成

src/cmoponents/SortableItem.tsx
"use client";

import React from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

type Props = {
  id: string;
};

export function SortableItem({ id }: Props) {
  // useSortableフックから並び替えに必要な情報を取得
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id: id });

  // transformとtransitionをCSSのスタイルとして適用
  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    // ドラッグ中は少し見た目を変える
    opacity: isDragging ? 0.5 : 1,
    zIndex: isDragging ? 10 : "auto",
  };

  return (
    // setNodeRefでこのdiv要素をdnd-kitが監視するDOMノードとして設定
    <div ref={setNodeRef} style={style} className="p-4 border rounded bg-white mb-2 touch-none">
      {/* 
        attributesとlistenersをハンドル(掴む部分)に適用する
        今回はアイテム全体を掴めるようにdivに適用
      */}
      <div {...attributes} {...listeners}>
        アイテム {id}
      </div>
    </div>
  );
}

useSortable(Hook)

並び替え可能なアイテムを作成するためのHooks

attributes:

roleやaria-属性など、ドラッグ可能な要素に付与すべき
アクセシビリティ関連の属性が含まれる

listeners:

ドラッグ操作を開始するためのイベントリスナー(onMouseDown, onTouchStart など)。
こちらを適用した要素がドラッグのハンドル(掴む部分)になります。

setNodeRef

dnd-kitがDOM要素を認識し、操作するために使用。
refプロパティに渡します。

transform

ドラッグ中の要素の移動量を表す {x, y} 形式のオブジェクト。
CSS.Transform.toString()を使ってCSSのtransformプロパティに変換する。

transition

ドラッグが終了し、要素が元の位置に戻る際のアニメーションを指定する。

isDragging

アイテムが現在ドラッグされているかどうかを判定するフラグ

3.2 リスト作成

src/components/SortableList.tsx
"use client";

import React, { useState } from "react";
import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  DragEndEvent,
} from "@dnd-kit/core";
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { SortableItem } from "./SortableItem";

export function SortableList() {
  // 並び替えるアイテムのリストをstateで管理
  const [items, setItems] = useState(["1", "2", "3"]);

  // センサー(どのような入力でドラッグを開始するか)の設定
  // ここではポインター(マウスやタッチ)とキーボード操作を有効にしている
  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  );

  // ドラッグ終了時の処理
  const handleDragEnd = (event: DragEndEvent) => {
    const { active, over } = event;

    // overがnullの場合(ドロップ先がない場合)は何もしない
    if (!over) {
      return;
    }

    // ドラッグ元とドロップ先が同じ場合は何もしない
    if (active.id !== over.id) {
      setItems((items) => {
        // 元のインデックスと新しいインデックスを取得
        const oldIndex = items.indexOf(active.id as string);
        const newIndex = items.indexOf(over.id as string);
        
        // arrayMoveユーティリティを使って配列を並び替える
        return arrayMove(items, oldIndex, newIndex);
      });
    }
  };

  return (
    // dnd-kitのコンテキストプロバイダー
    // sensors: どの入力方法でドラッグ操作を認識するかを設定
    // collisionDetection: どの要素と衝突しているかを判断するアルゴリズム
    // onDragEnd: ドラッグが終了したときに呼ばれる関数
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragEnd={handleDragEnd}
    >
      {/* 
        SortableContext: 並び替え可能な要素のコンテキストを提供
        items: 並び替え可能なアイテムのIDの配列
        strategy: 並び替えの戦略(ここでは垂直方向のリスト)
      */}
      <SortableContext items={items} strategy={verticalListSortingStrategy}>
        <div className="w-full max-w-xs p-4 bg-gray-100 rounded">
          {items.map((id) => (
            // 並び替え可能な各アイテムをレンダリング
            <SortableItem key={id} id={id} />
          ))}
        </div>
      </SortableContext>
    </DndContext>
  );
}

3.3 page.tsxに組み込み

src/app/page.tsx
import { SortableList } from "@/components/SortableList";

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-24">
      <h1 className="text-4xl font-bold mb-8">dnd-kit サンプル</h1>
      <SortableList />
    </main>
  );
}

4.動作確認

# localhost:3000で動作確認
npm run dev

スクリーンショット 2025-08-28 14.39.57.png

ドラッグ&ドロップで並び替えも可能
スクリーンショット 2025-08-28 14.41.28.png

5.まとめ

dnd-kitを使うとTrelloのようなカンバンや
タスク管理、ファイルアップロードなど色々と使えるケースがあるので、
使うケースが発生したら都度動作確認しながら理解を深めていきたいです。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?