Docs/Guides/Drag

Drag

drag opts a motion element into pointer-based dragging. Pass true, 'x', or 'y' and motion tracks pointer movement, then applies inertia on release.

<motion.div drag />
drag
drag-basic.tsxOpen ↗
import { motion } from 'motion-solidjs'

export const meta = {
  slug: 'drag-basic',
  title: 'Drag — basic',
  category: 'drag',
  description: 'A draggable block with momentum and a soft spring back.',
  tag: 'drag',
} as const

export default function DragBasic() {
  return (
    <motion.div
      drag
      dragMomentum
      class="h-20 w-20 cursor-grab rounded-3xl bg-grad-rose shadow-glow active:cursor-grabbing"
      whileTap={{ scale: 1.1 }}
      transition={{ type: 'spring', stiffness: 320, damping: 22 }}
    />
  )
}

Constraints

Constrain with a box, an element ref, or both:

<motion.div drag dragConstraints={{ left: 0, right: 200, top: 0, bottom: 80 }} />
dragConstraints
drag-constraints.tsxOpen ↗
import { motion } from 'motion-solidjs'

export const meta = {
  slug: 'drag-constraints',
  title: 'Drag — constraints',
  category: 'drag',
  description: 'Numerical constraints limit how far the element travels.',
  tag: 'dragConstraints',
} as const

export default function DragConstraints() {
  return (
    <motion.div
      drag="x"
      dragConstraints={{ left: -80, right: 80 }}
      dragElastic={0.2}
      class="h-16 w-16 cursor-grab rounded-2xl bg-grad-mint shadow-glow active:cursor-grabbing"
    />
  )
}

Snap & momentum

dragSnapToOrigin returns the element to its start point after release. dragMomentum controls post-flick decay, and dragElastic controls how far the element can stretch past its constraints.

snap
drag-snap.tsxOpen ↗
import { motion } from 'motion-solidjs'
import { createSignal } from 'solid-js'

export const meta = {
  slug: 'drag-snap',
  title: 'Drag — snap to slots',
  category: 'drag',
  description: 'On drag end, snap to the nearest of three positions.',
  tag: 'snap',
} as const

const slots = [-80, 0, 80]

export default function DragSnap() {
  const [x, setX] = createSignal(0)

  return (
    <div class="flex flex-col items-center gap-4">
      <div class="relative flex h-16 w-56 items-center justify-between rounded-full border border-border bg-card px-3">
        {slots.map((s) => (
          <span
            class="h-2 w-2 rounded-full bg-fg-dim"
            style={{ transform: `translateX(${s - slots[1]}px)` }}
          />
        ))}
        <motion.div
          drag="x"
          dragConstraints={{ left: -90, right: 90 }}
          dragElastic={0.2}
          class="absolute h-12 w-12 cursor-grab rounded-full bg-grad-rose shadow-glow active:cursor-grabbing"
          animate={{ x: x() }}
          onDragEnd={(_, info) => {
            const final = x() + info.offset.x
            const nearest = slots.reduce((a, b) =>
              Math.abs(b - final) < Math.abs(a - final) ? b : a,
            )
            setX(nearest)
          }}
          transition={{ type: 'spring', stiffness: 380, damping: 26 }}
        />
      </div>
    </div>
  )
}

Reorder a list

<Reorder> builds on drag to provide sortable lists with layout animations.

Reorder
  • Rose
  • Violet
  • Mint
  • Amber
drag-reorder.tsxOpen ↗
import { Reorder } from 'motion-solidjs'
import { For, createSignal } from 'solid-js'

export const meta = {
  slug: 'drag-reorder',
  title: 'Reorder list',
  category: 'drag',
  description: 'Drag the rows to reorder them — values stay in sync.',
  tag: 'Reorder',
} as const

const initial = [
  { id: 'rose', label: 'Rose', color: 'bg-grad-rose' },
  { id: 'violet', label: 'Violet', color: 'bg-grad-violet' },
  { id: 'mint', label: 'Mint', color: 'bg-grad-mint' },
  { id: 'amber', label: 'Amber', color: 'bg-grad-amber' },
]

export default function DragReorder() {
  const [items, setItems] = createSignal(initial)

  return (
    <Reorder.Group axis="y" values={items()} onReorder={setItems} class="flex w-56 flex-col gap-2">
      <For each={items()}>
        {(it) => (
          <Reorder.Item
            value={it}
            class="flex cursor-grab items-center gap-3 rounded-2xl border border-border bg-card px-3 py-2 text-xs active:cursor-grabbing"
            whileTap={{ scale: 1.04 }}
          >
            <span class={`h-6 w-6 rounded-lg ${it.color}`} />
            <span class="font-medium">{it.label}</span>
            <span class="ml-auto text-fg-dim">⇅</span>
          </Reorder.Item>
        )}
      </For>
    </Reorder.Group>
  )
}

See also