# Draggable Sortable

Source: https://payloadcms.com/docs/ui-components/draggable-sortable

Use `DraggableSortable` with `DraggableSortableItem` to add pointer, touch, and keyboard reordering to custom Admin Panel interfaces.

## Import

```tsx
import { DraggableSortable, DraggableSortableItem } from '@payloadcms/ui'
```

## Basic usage

**Implementation**

```tsx
'use client'

import {
  Button,
  DraggableSortable,
  DraggableSortableItem,
  DragHandleIcon,
} from '@payloadcms/ui'
import { useState } from 'react'

const initialItems = [
  { id: 'overview', label: 'Overview' },
  { id: 'details', label: 'Details' },
  { id: 'settings', label: 'Settings' },
]

const moveItem = (items, moveFromIndex, moveToIndex) => {
  const nextItems = [...items]
  const [movedItem] = nextItems.splice(moveFromIndex, 1)
  nextItems.splice(moveToIndex, 0, movedItem)
  return nextItems
}

export function SortableList() {
  const [items, setItems] = useState(initialItems)

  return (
    <DraggableSortable
      ids={items.map(({ id }) => id)}
      onDragEnd={({ moveFromIndex, moveToIndex }) => {
        setItems((current) => moveItem(current, moveFromIndex, moveToIndex))
      }}
    >
      {items.map(({ id, label }) => (
        <DraggableSortableItem id={id} key={id}>
          {({ attributes, isDragging, listeners, setNodeRef, transform, transition }) => (
            <div
              ref={setNodeRef}
              style={{ opacity: isDragging ? 0.6 : 1, transform, transition }}
            >
              <span>{label}</span>
              <Button
                aria-label={`Reorder ${label}`}
                buttonStyle="icon-label"
                extraButtonProps={{ ...attributes, ...listeners }}
                icon={<DragHandleIcon />}
                margin={false}
              />
            </div>
          )}
        </DraggableSortableItem>
      ))}
    </DraggableSortable>
  )
}
```

**Styling**

Style the list and row wrappers while leaving the drag transform and transition inline on each item. Payload theme variables keep the surface consistent across light and dark modes.

```css
.sortable-list {
  display: grid;
  gap: calc(var(--base) / 2);
}

.sortable-item {
  align-items: center;
  background: var(--theme-elevation-50);
  border: 1px solid var(--theme-elevation-200);
  display: flex;
  justify-content: space-between;
  padding: calc(var(--base) / 2) var(--base);
}
```

- `--theme-elevation-50`: Sortable item background.
- `--theme-elevation-200`: Sortable item border.
- `--base`: Spacing within and between items.

`DraggableSortable` coordinates the list and reports the original and destination indexes through `onDragEnd`. Your component remains responsible for updating local state and persisting the new order when necessary.

Each `DraggableSortableItem` exposes the attributes, listeners, ref, transform, and transition needed to render an item. Apply the listeners to a visible drag handle so the rest of the row can retain its own interactions.

## DraggableSortable props

| Prop          | Type                                              | Description                                    |
| ------------- | ------------------------------------------------- | ---------------------------------------------- |
| `children*`   | `ReactNode`                                       | Renders the sortable items.                    |
| `ids*`        | `string[]`                                        | Lists item IDs in their current display order. |
| `onDragEnd*`  | `({ moveFromIndex, moveToIndex, event }) => void` | Handles a completed reorder.                   |
| `onDragStart` | `({ id, event }) => void`                         | Handles the beginning of a drag.               |
| `className`   | `string`                                          | Adds a class to the sortable list container.   |

## DraggableSortableItem props

| Prop        | Type                           | Description                                                 |
| ----------- | ------------------------------ | ----------------------------------------------------------- |
| `id*`       | `string`                       | Matches an entry in the parent `ids` array.                 |
| `children*` | `(sortableState) => ReactNode` | Renders the item with its drag attributes and visual state. |
| `disabled`  | `boolean`                      | Prevents the item from being reordered.                     |

_\* An asterisk denotes that a prop is required._

## Accessibility

- Put the supplied attributes and listeners on a keyboard-focusable drag handle.
- Give the handle an accessible name that identifies the item it moves.
- Persist the resulting order only after `onDragEnd` supplies a valid destination.

These components configure their own drag-and-drop context and require no Payload providers.
