<!-- Source: https://motion.svelte.page/docs/reorder -->

# Reorder

> Drag-to-reorder lists and wrapped grids with automatic axis detection, RTL, layout animations, and edge auto-scroll

**Source:** [https://motion.svelte.page/docs/reorder](https://motion.svelte.page/docs/reorder)

---

`Reorder.Group` and `Reorder.Item` create drag-to-reorder lists and wrapped grids — sortable tabs, dashboards, playlists — with a couple of lines of markup. The group detects its layout axis, dragged items stay pinned under the cursor, and siblings spring out of the way.

```svelte
<script lang="ts">
    import { Reorder } from '@humanspeak/svelte-motion'

    let items = $state([0, 1, 2, 3])
</script>

<Reorder.Group values={items} onReorder={(next) => (items = next)}>
    {#each items as item (item)}
        <Reorder.Item value={item}>{item}</Reorder.Item>
    {/each}
</Reorder.Group>
```

## Usage

Every reorderable list needs three things wired together:

1. **`values`** — the array driving the list, passed to `Reorder.Group`.
2. **`onReorder`** — a callback that receives the new order. Assign it back to the state that renders the list.
3. **`value`** — each `Reorder.Item` declares which entry it represents. Use the same value as the `{#each}` key.

```svelte
<script lang="ts">
    import { Reorder } from '@humanspeak/svelte-motion'

    let tabs = $state(['Home', 'Search', 'Library'])
</script>

<Reorder.Group axis="x" values={tabs} onReorder={(next) => (tabs = next)}>
    {#each tabs as tab (tab)}
        <Reorder.Item value={tab}>{tab}</Reorder.Item>
    {/each}
</Reorder.Group>
```

The group renders a `ul` and items render `li` by default. Change either with `as`:

```svelte
<Reorder.Group as="div" values={items} onReorder={handleReorder}>
    <Reorder.Item as="article" value={item} />
</Reorder.Group>
```

## Axis

Omit `axis` to detect `"x"`, `"y"`, or `"xy"` from measured item geometry. Detection starts safely at `"y"` until enough items are measured. Pass an explicit axis when you want to override the layout:

```svelte
<Reorder.Group axis="x" values={tabs} onReorder={setTabs} />
```

Use `axis="xy"` for a wrapped layout. A single drag can move an item between columns and rows while displaced siblings animate into their new slots:

```svelte
<Reorder.Group
    axis="xy"
    values={tiles}
    onReorder={(next) => (tiles = next)}
    style="display: flex; flex-wrap: wrap"
>
    {#each tiles as tile (tile.id)}
        <Reorder.Item value={tile}>{tile.label}</Reorder.Item>
    {/each}
</Reorder.Group>
```

Explicit `"x"` and `"y"` axes lock item dragging to one direction. To let an item move freely while the group still reorders on one axis, pass `drag` to the item:

```svelte
<Reorder.Item value={item} drag>{item}</Reorder.Item>
```

## Layout animations

Items carry `layout` automatically, so displaced siblings FLIP to their new slots while the drag is live, and the dragged item springs into its new slot on release. If an item changes size while reordering, pass `layout="position"` to skip size projection:

```svelte
<Reorder.Item value={item} layout="position">{item}</Reorder.Item>
```

Items float above their siblings while dragging via an automatic `z-index`. To make that work, items default to `position: relative` — override it via `style` if your layout needs something else, but keep items positioned so the floating z-index applies.

## Scrollable lists

Lists taller than their container work out of the box — dragging an item near the container's edge auto-scrolls it, so long lists can be traversed in a single gesture.

Mark the scrollable container with `layoutScroll` so layout measurements stay correct while it scrolls:

```svelte
<script lang="ts">
    import { motion, Reorder } from '@humanspeak/svelte-motion'

    let items = $state([...Array(20).keys()])
</script>

<motion.div layoutScroll style="height: 300px; overflow-y: scroll">
    <Reorder.Group values={items} onReorder={(next) => (items = next)}>
        {#each items as item (item)}
            <Reorder.Item value={item}>{item}</Reorder.Item>
        {/each}
    </Reorder.Group>
</motion.div>
```

The window works as the scroll container too — lists below the fold reorder correctly at any scroll position, and dragging near the viewport edge scrolls the page.

For `axis="xy"`, only the dominant velocity axis can auto-scroll on each drag frame. This prevents a diagonal gesture from double-scrolling horizontal and vertical containers; equal x/y speeds prefer vertical scrolling.

## Right-to-left layouts

Horizontal and wrapped groups read `direction` from the rendered group. In an RTL region, visual insertion direction is reversed automatically while `values` remains in logical order:

```svelte
<Reorder.Group dir="rtl" values={items} onReorder={setItems}>
    {#each items as item (item)}
        <Reorder.Item value={item}>{item}</Reorder.Item>
    {/each}
</Reorder.Group>
```

Direction is read from the group itself, so nested LTR/RTL regions work without changing the document direction or reversing consumer data.

## Observing the drag offset

Items accept external MotionValues for `x`/`y` through `style`, so you can observe or drive the live drag offset — for example to fade a shadow in while an item is lifted:

```svelte
<script lang="ts">
    import { Reorder, useMotionValue, useTransform } from '@humanspeak/svelte-motion'

    const y = useMotionValue(0)
    const boxShadow = useTransform(y, (latest) =>
        `0 ${Math.min(Math.abs(latest) / 4, 8)}px 16px rgba(0,0,0,0.2)`
    )
</script>

<Reorder.Item value={item} style={{ y, boxShadow }}>{item}</Reorder.Item>
```

## Drag props and callbacks

Items are motion components underneath — every [drag prop](/docs/drag) (`whileDrag`, `dragListener`, `dragControls`, `onDragStart`, …) passes straight through:

```svelte
<Reorder.Item
    value={item}
    whileDrag={{ scale: 1.03 }}
    onDragStart={() => (grabbed = item)}
    onDragEnd={() => (grabbed = null)}
>
    {item}
</Reorder.Item>
```

`dragSnapToOrigin` is managed by Reorder itself — the "origin" an item snaps back to is its current slot, which updates as the order changes.

## Notes

- One-dimensional reordering moves values only while the pointer moves — a swap fires when the dragged item's edge crosses the midpoint of its neighbor.
- `values` can hold any type — strings, numbers, or objects (compared by reference). Keep the `{#each}` key and the item `value` in sync.
- Wrapped `axis="xy"` layouts choose the closest visual row and horizontal slot from complete item measurements.

## Props

### Reorder.Group

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `values` | `V[]` | — | The current order of item values (required) |
| `onReorder` | `(newOrder: V[]) => void` | — | Fires with the new order after a swap (required) |
| `axis` | `'x' \| 'y' \| 'xy'` | detected | Override the detected reorder axis; use `'xy'` for wrapped layouts |
| `as` | `string` | `'ul'` | The HTML element to render |

All other motion props are forwarded to the underlying motion element.

### Reorder.Item

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `V` | — | The entry in `values` this item represents (required) |
| `as` | `string` | `'li'` | The HTML element to render |
| `layout` | `true \| 'position'` | `true` | Layout animation mode while slots shuffle |
| `drag` | `boolean \| 'x' \| 'y'` | group axis | Override the axis lock (e.g. `drag` frees both axes) |

All other motion props — including every drag prop and callback — are forwarded to the underlying motion element.

## Related

- [Drag](/docs/drag) — The gesture system Reorder builds on
- [Layout Animations](/docs/layout-animations) — How siblings FLIP between slots
- [Motion values](/docs/motion-values) — Observe the live drag offset

---

Based on [Motion's Reorder](https://motion.dev/docs/react-reorder) API.
