motion-solid

Reorder

Reorder.Group and Reorder.Item make a list that can be reordered by dragging its items. They're motion components: dragging uses drag, and items move with layout animations.

Usage

The group gets the list as values and a callback, onReorder, that receives the new order. Each item gets the value it represents:

import { Reorder } from "@motion-s/solid"
import { createSignal, For } from "solid-js"

const [items, setItems] = createSignal(["Apples", "Bread", "Coffee"])

<Reorder.Group axis="y" values={items()} onReorder={setItems}>
    <For each={items()}>
        {(item) => <Reorder.Item value={item}>{item}</Reorder.Item>}
    </For>
</Reorder.Group>
  • Apples
  • Bread
  • Coffee
  • Dates

While an item is dragged past its neighbours, onReorder is called with the new order. Rendering that order moves the other items with a layout animation. When the item is released, it animates into its slot.

Values must be unique: they identify the items. Use objects or ids rather than labels that can repeat.

Stores

values can be an array in a store. The group also follows changes made to the array in place:

const [state, setState] = createStore({ items: ["Apples", "Bread", "Coffee"] })

const reorder = (order: string[]) =>
    setState((draft) => {
        draft.items.splice(0, draft.items.length, ...order)
    })

<Reorder.Group values={state.items} onReorder={reorder}>
    <For each={state.items}>
        {(item) => <Reorder.Item value={item}>{item}</Reorder.Item>}
    </For>
</Reorder.Group>

Drag handles

By default, the whole item starts a drag. To drag it by a handle only, turn off the item's own listener with dragListener={false}, and start the drag from the handle with useDragControls:

const controls = useDragControls()

<Reorder.Item value={item} dragListener={false} dragControls={controls}>
    {item}
    <span onPointerDown={(event) => controls.start(event)}>⠿</span>
</Reorder.Item>
  • First
  • Second
  • Third
  • Fourth

Give the handle touch-action: none (touch-none in Tailwind), or touch devices scroll the page instead of dragging.

Scrolling

When the group is inside a scrolling container, dragging an item close to the container's edge scrolls it.

Reorder.Group props

PropTypeDefaultDescription
valuesV[]The values of the items, in their current order. Required.
onReorder(newOrder: V[]) => voidCalled with the new order while an item is dragged. Required.
axis"x" | "y" | "xy"detected from the items' layoutThe direction of the list. Items can be dragged along it, "xy" for both directions (a wrapping grid).
aselement tag name"ul"The element to render. Read once.

It also takes the props of a motion component, like class and style.

Reorder.Item props

PropTypeDefaultDescription
valueVThe value this item represents, from the group's values. Required.
layouttrue | "position"trueIts layout animation. "position" stops it from animating its size.
aselement tag name"li"The element to render. Read once.

It also takes the props of a motion component: whileDrag to style the dragged item, drag to change the drag direction (by default, the group's axis), onDragStart and onDragEnd, dragListener and dragControls for handles.

Reorder.Item must be inside a Reorder.Group.