![image](/plants/react-dnd-kit-sortable-list/1-sample.gif)

Drag-and-drop is a common feature in modern web applications, letting users reorder items seamlessly. In this tutorial, we'll explore how to implement drag-and-drop functionality in a React application using the `dnd-kit/core` library. We'll use this library's core components to build a simple sortable list of draggable items.

## Project Setup

Before diving into the implementation, let's install the required dependencies. Install `@dnd-kit/core` in your React project.

```bash
npm i @dnd-kit/core @dnd-kit/sortable
```

From here, we'll build out the components for our draggable list.

## Creating the DraggableList Component

The `DraggableList` component acts as the container for the draggable items. Here's the code for the `DraggableList.tsx` file:

`DraggableList.tsx(DraggableList)`

```tsx
import {
  DndContext,
  KeyboardSensor,
  PointerSensor,
  UniqueIdentifier,
  closestCenter,
  useSensor,
  useSensors
} from '@dnd-kit/core';
import {
  SortableContext,
  horizontalListSortingStrategy,
  rectSortingStrategy,
  sortableKeyboardCoordinates,
  useSortable,
  verticalListSortingStrategy
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { ComponentProps, FC, ReactNode, useMemo } from 'react';

export interface HasId {
  id: UniqueIdentifier;
}

type Props<T extends HasId> = {
  items: T[];
  onDragStart?: ComponentProps<typeof DndContext>['onDragStart'];
  onDragEnd?: ComponentProps<typeof DndContext>['onDragEnd'];
  onDragOver?: ComponentProps<typeof DndContext>['onDragOver'];
  layout: 'horizontal' | 'vertical' | 'grid';
  children: ReactNode;
};
// ドラッグ&ドロップ可能なリストアイテム
export const DraggableList: FC<Props<HasId>> = ({
  items,
  onDragStart,
  onDragEnd,
  onDragOver,
  layout,
  children
}) => {
  // ドラッグ&ドロップする時に許可する入力
  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates
    })
  );

  // リストの種類
  const strategy = useMemo(() => {
    switch (layout) {
      case 'horizontal':
        return horizontalListSortingStrategy;
      case 'vertical':
        return verticalListSortingStrategy;
      case 'grid':
      default:
        return rectSortingStrategy;
    }
  }, [layout]);

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragStart={onDragStart}
      onDragEnd={onDragEnd}
      onDragOver={onDragOver}
    >
      <SortableContext items={items} strategy={strategy}>
        <ul>{children}</ul>
      </SortableContext>
    </DndContext>
  );
};
```

This component sets up the `DndContext` and `SortableContext` provided by `@dnd-kit/core`. It also defines the `DraggableItem` component that wraps each item in the list.

## Building the Draggable Item

Each draggable item in the list is represented by the `DraggableItem` component. It also includes a `DraggableHandle` to improve the user interaction. Here's the code:

`DraggableList.tsx(DraggableItem)`

```tsx
// ドラッグ&ドロップ可能なリストアイテム
export const DraggableItem: FC<{
  id: HasId['id'];
  children: ReactNode;
}> = ({ id, children }) => {
  const { setNodeRef, transform, transition } = useSortable({ id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition
  };

  return (
    <li ref={setNodeRef} style={style}>
      {children}
    </li>
  );
};

// MyDraggableItemをドラッグ&ドロップするためのハンドル
export const DraggableHandle: FC<{
  id: HasId['id'];
  children: ReactNode;
}> = ({ id, children }) => {
  const { attributes, listeners } = useSortable({ id });

  return (
    <div {...attributes} {...listeners}>
      {children}
    </div>
  );
};
```

This component uses the `useSortable` hook from `@dnd-kit/core` to handle the drag-and-drop behavior.

## Implementing Drag-and-Drop in the Page Component

In the main page component (`page.tsx`), we import and use `DraggableList` to build a draggable list of products. Here's the code:

> This uses Next.js, so it's named `page.tsx`, but it works just as well as a regular React component.

```jsx
'use client';

import { DragEndEvent, DragStartEvent } from '@dnd-kit/core';
import { faBars } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC, useCallback, useState } from 'react';
import { DraggableHandle, DraggableItem, DraggableList } from './_components/DraggableList';

// リソース:商品の型
type Product = {
  id: string;
  name: string;
  price: number;
};

const Products = [
  {
    id: '1',
    name: '商品1',
    price: 100
  },
  {
    id: '2',
    name: '商品2',
    price: 200
  },
  {
    id: '3',
    name: '商品3',
    price: 300
  }
];

// 商品を表示するリストアイテム
const ProductItem: FC<{
  product: Product;
}> = ({ product }) => (
  <DraggableItem id={product.id}>
    <div className="m-4 h-[100px] w-[200px] items-center rounded-md border-2 p-2 shadow-sm">
      <div className="flex space-x-4">
        <DraggableHandle id={product.id}>
          <FontAwesomeIcon icon={faBars} className="m-4" size="lg" />
        </DraggableHandle>
        <p>{product.name}</p>
        <p>¥ {product.price}</p>
      </div>
    </div>
  </DraggableItem>
);

// ドラッグ&ドロップ可能な商品リスト
const Page = () => {
  // 一覧表示する商品群
  const [products, setProducts] = useState<Product[]>(Products);

  const onDragStart = useCallback((e: DragStartEvent) => {
    console.log('onDragStart', e);
  }, []);

  const onDragEnd = useCallback((e: DragEndEvent) => {
    console.log('onDragEnd', e);
  }, []);

  return (
    <DraggableList
      items={products}
      onDragStart={onDragStart}
      onDragEnd={onDragEnd}
      layout={'vertical'}
    >
      {products.map((product) => (
        <ProductItem key={product.id} product={product} />
      ))}
    </DraggableList>
  );
};

export default Page;
```

This component renders a list of products, and the `onDragStart` and `onDragEnd` callbacks log the corresponding events to the console.

## Styling and Customization

Feel free to customize the styling of the draggable items to match your application's design. The provided code includes basic styling using Tailwind CSS.

## Summary

In this tutorial, we implemented a simple draggable list using `react` and `@dnd-kit/core`. This library provides a robust toolset for handling drag-and-drop interactions, making it easy to build a seamless user experience.

You can take this implementation further with animations, custom drag handles, and more. Explore the `@dnd-kit/core` documentation for additional options and possibilities.
