---
title: Sortable
description: >-
  A compound drag-and-drop component for reordering items, with orientation and
  constraint controls.
---

# Sortable

## Overview

Sortable is a layout component that enables drag-and-drop reordering of items. It supports items arranged as vertical lists, horizontal rows, or grids.

```tsx
const [items, setItems] = useState([
	{ id: 1, label: "A" },
	{ id: 2, label: "B" },
	{ id: 3, label: "C" },
]);

<Sortable.Root items={items} onItemsChange={setItems}>
	{items.map((item) => (
		<Sortable.Item key={item.id} id={item.id}>
			<div>{item.label}</div>
		</Sortable.Item>
	))}
</Sortable.Root>
```

## Usage Guidelines

- **Use sorting for meaningful order:** Use a sortable layout when item order should be user-defined and affects priority, sequence, or organization.
- **Use drag handles when needed:** Use `Sortable.Handle` when items contain other interactive controls or when a smaller drag target provides a clearer affordance.
- **Keep items easy to identify:** Make sortable items distinct and scannable so users can track what they are moving.

## API Reference

### `Sortable.Root`

Provides drag-and-drop context and ordering logic for sortable items.

| Prop                | Type                                               | Default      |
| ------------------- | -------------------------------------------------- | ------------ |
| `items`             | `Array<{ id: string \| number }>`                  | `undefined`  |
| `onItemsChange`     | `(items: Array<{ id: string \| number }>) => void` | `undefined`  |
| `children`          | `ReactNode`                                        | `undefined`  |
| `orientation`       | `"vertical" \| "horizontal" \| "free"`             | `"vertical"` |
| `constrainToParent` | `boolean`                                          | `true`       |

### `Sortable.Item`

Registers an item in the sortable context and applies drag state.

| Prop        | Type                                | Default     |
| ----------- | ----------------------------------- | ----------- |
| `id`        | `string \| number`                  | `undefined` |
| `children`  | `ReactNode`                         | `undefined` |
| `className` | `string`                            | `undefined` |
| `style`     | `CSSProperties`                     | `undefined` |
| `render`    | `RenderProp<{ dragging: boolean }>` | `undefined` |

### `Sortable.Handle`

Optional drag handle that limits dragging to a specific child element.

| Prop        | Type         | Default     |
| ----------- | ------------ | ----------- |
| `children`  | `ReactNode`  | `undefined` |
| `className` | `string`     | `undefined` |
| `render`    | `RenderProp` | `undefined` |

## Examples

### Orientation

The `orientation` property controls how items can be sorted. Sortable supports sorting in a vertical column, a horizontal row, or even across a grid.

```tsx
const [items, setItems] = useState([
	{ id: 1, label: "A" },
	{ id: 2, label: "B" },
	{ id: 3, label: "C" },
]);

<Sortable.Root orientation="horizontal" items={items} onItemsChange={setItems}>
	<Stack orientation="horizontal" gap={4}>
		{items.map((item) => (
			<Sortable.Item key={item.id} id={item.id}>
				<Box label={item.label} height="160px" width="80px" />
			</Sortable.Item>
		))}
	</Stack>
</Sortable.Root>
```

### Drag handles

By default, the entire `Sortable.Item` is draggable. Use `Sortable.Handle` to only allow certain parts of a sortable item to be dragged.

```tsx
const [items, setItems] = useState([
	{ id: 1, label: "A" },
	{ id: 2, label: "B" },
	{ id: 3, label: "C" },
]);

<Sortable.Root items={items} onItemsChange={setItems}>
	{items.map((item) => (
		<Sortable.Item key={item.id} id={item.id}>
			<Sortable.Handle />
			<div>{item.label}</div>
		</Sortable.Item>
	))}
</Sortable.Root>
```

### Constraints

Items can be dragged beyond the parent's boundary by disabling the `constrainToParent` property.

```tsx
const [items, setItems] = useState([
	{ id: 1, label: "A" },
	{ id: 2, label: "B" },
	{ id: 3, label: "C" },
]);

<Sortable.Root constrainToParent={false} items={items} onItemsChange={setItems}>
	{items.map((item) => (
		<Sortable.Item key={item.id} id={item.id}>
			<div>{item.label}</div>
		</Sortable.Item>
	))}
</Sortable.Root>
```

