logo

Drag

Any motion component can be made draggable with the drag prop. The component gets full physics-based momentum, elastic constraints, and gesture callbacks.

<motion.div drag />
<motion.div drag />

Drag the box around

Usage

Set drag to true to enable dragging on both axes:

<motion.div drag />
<motion.div drag />

To restrict dragging to a single axis, pass "x" or "y":

<motion.div drag="x" />
<motion.div drag="x" />

Constraints

Drag movement can be constrained with the dragConstraints prop. Constraints can be defined as pixel offsets from the element’s origin, or as a reference to another element.

Pixel constraints

Pass an object with top, left, right, and bottom values (in pixels):

<motion.div
    drag
    dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
/>
<motion.div
    drag
    dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
/>

Element constraints

Pass an element reference to constrain dragging within that element’s bounding box:

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

    let bounds: HTMLElement | null = null
</script>

<div bind:this={bounds}>
    <motion.div drag dragConstraints={bounds} />
</div>
<script lang="ts">
    import { motion } from '@humanspeak/svelte-motion'

    let bounds: HTMLElement | null = null
</script>

<div bind:this={bounds}>
    <motion.div drag dragConstraints={bounds} />
</div>

Constrained to the dashed area

Elastic

By default, dragging beyond constraints has an elastic feel. Control this with dragElastic:

<!-- No elasticity — hard stop at constraints -->
<motion.div drag dragConstraints={{ left: -100, right: 100 }} dragElastic={0} />

<!-- Full elasticity (default ~0.35) -->
<motion.div drag dragConstraints={{ left: -100, right: 100 }} dragElastic={1} />
<!-- No elasticity — hard stop at constraints -->
<motion.div drag dragConstraints={{ left: -100, right: 100 }} dragElastic={0} />

<!-- Full elasticity (default ~0.35) -->
<motion.div drag dragConstraints={{ left: -100, right: 100 }} dragElastic={1} />

Set dragElastic to 0 for a hard boundary, or 1 for maximum stretch. Values between 0 and 1 control the amount of elastic overdrag.

Momentum

After releasing a drag, the element continues with momentum by default. Disable this with dragMomentum:

<motion.div drag dragMomentum={false} />
<motion.div drag dragMomentum={false} />

Fine-tune the physics with dragTransition:

<motion.div
    drag
    dragTransition={{
        bounceStiffness: 300,
        bounceDamping: 20,
        power: 0.8,
        timeConstant: 200
    }}
/>
<motion.div
    drag
    dragTransition={{
        bounceStiffness: 300,
        bounceDamping: 20,
        power: 0.8,
        timeConstant: 200
    }}
/>

Snap to origin

Set dragSnapToOrigin to spring the element back to its starting position on release:

<motion.div
    drag
    dragSnapToOrigin
    whileDrag={{ scale: 1.05, cursor: 'grabbing' }}
/>
<motion.div
    drag
    dragSnapToOrigin
    whileDrag={{ scale: 1.05, cursor: 'grabbing' }}
/>

Direction lock

When dragDirectionLock is enabled and both axes are allowed, the drag locks to whichever axis the user moves first (threshold: 4px):

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

    let lockedAxis = $state<'x' | 'y' | null>(null)
</script>

<motion.div
    drag
    dragDirectionLock
    onDirectionLock={(axis) => (lockedAxis = axis)}
/>
<script lang="ts">
    import { motion } from '@humanspeak/svelte-motion'

    let lockedAxis = $state<'x' | 'y' | null>(null)
</script>

<motion.div
    drag
    dragDirectionLock
    onDirectionLock={(axis) => (lockedAxis = axis)}
/>

Drag controls

Use createDragControls to start a drag from an external element, like a drag handle:

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

    const controls = createDragControls()
</script>

<button onpointerdown={(e) => controls.start(e)}>Drag handle</button>

<motion.div drag dragControls={controls} dragListener={false} />
<script lang="ts">
    import { motion, createDragControls } from '@humanspeak/svelte-motion'

    const controls = createDragControls()
</script>

<button onpointerdown={(e) => controls.start(e)}>Drag handle</button>

<motion.div drag dragControls={controls} dragListener={false} />

Setting dragListener={false} prevents the element itself from initiating the drag — only the external handle will work.

The start method accepts an optional options object:

controls.start(event, { snapToCursor: true })
controls.start(event, { snapToCursor: true })

While dragging

Use whileDrag to animate the element while a drag gesture is active:

<motion.div
    drag
    whileDrag={{ scale: 1.1, boxShadow: '0 8px 24px rgba(0,0,0,0.2)' }}
/>
<motion.div
    drag
    whileDrag={{ scale: 1.1, boxShadow: '0 8px 24px rgba(0,0,0,0.2)' }}
/>

Propagation

By default, drag events propagate to parent drag components. Set dragPropagation={false} to prevent this:

<motion.div drag>
    <motion.div drag dragPropagation={false} />
</motion.div>
<motion.div drag>
    <motion.div drag dragPropagation={false} />
</motion.div>

Callbacks

Drag provides several lifecycle callbacks. Each receives the native PointerEvent and a DragInfo object:

<motion.div
    drag
    onDragStart={(event, info) => console.log('Start', info.point)}
    onDrag={(event, info) => console.log('Move', info.delta)}
    onDragEnd={(event, info) => console.log('End', info.velocity)}
    onDragTransitionEnd={() => console.log('Momentum settled')}
/>
<motion.div
    drag
    onDragStart={(event, info) => console.log('Start', info.point)}
    onDrag={(event, info) => console.log('Move', info.delta)}
    onDragEnd={(event, info) => console.log('End', info.velocity)}
    onDragTransitionEnd={() => console.log('Momentum settled')}
/>

DragInfo

The info object passed to drag callbacks contains:

PropertyTypeDescription
point{ x, y }Current pointer position
delta{ x, y }Change since the last drag event (per-frame delta)
offset{ x, y }Cumulative transform offset since drag start
velocity{ x, y }Current velocity in px/s

Props

Drag

PropTypeDefaultDescription
dragboolean \| 'x' \| 'y'falseEnable dragging. true for both axes, or restrict to one axis
dragConstraints{ top?, left?, right?, bottom? } \| HTMLElementPixel bounds or element reference to constrain drag
dragElasticnumber0.35Elasticity when overdragging. 0 = hard stop, 1 = full stretch
dragMomentumbooleantrueApply momentum after release
dragTransitionDragTransitionPhysics configuration for momentum and boundaries
dragSnapToOriginbooleanfalseSpring back to starting position on release
dragDirectionLockbooleanfalseLock to first detected axis of movement
dragPropagationbooleantrueAllow drag events to bubble to parent drag elements
dragListenerbooleantrueEnable the built-in drag listener on the element
dragControlsDragControlsExternal controls for programmatic drag initiation
whileDragMotionWhileDragAnimation target while drag is active

Callbacks

PropTypeDescription
onDragStart(event, info) => voidFires when drag begins
onDrag(event, info) => voidFires on every drag movement
onDragEnd(event, info) => voidFires when drag ends
onDirectionLock(axis: 'x' \| 'y') => voidFires when direction is locked
onDragTransitionEnd() => voidFires when post-drag momentum settles

DragTransition

PropertyTypeDefaultDescription
bounceStiffnessnumber200Spring stiffness at constraint boundaries
bounceDampingnumber40Damping at constraint boundaries
powernumber0.8Momentum power factor
timeConstantnumber700Exponential decay time constant (ms)
restDeltanumber0.5Distance threshold for “at rest”
restSpeednumber10Velocity threshold for “at rest”
minnumberMinimum value for axis
maxnumberMaximum value for axis

Related


Based on Motion’s drag API.