Documentation

Documentation

Drag animation

Drag animations allow users to move elements with their pointer.

Motion provides a suite of features to create app-quality drag animations with a simple API:

  • Momentum

  • Axis control

  • Elastic constraints

  • Direction locking

  • Optional imperative start/stop controls

Although browsers provide a native Drag and Drop API, it can be challenging to create a pleasant experience, with an odd "ghost image" effect. On the other hand, it also contains native dropzone functionality - which isn't yet in Motion.

In this guide we'll take a look at all Motion's drag features and how to customise them.

The drag prop

The simplest way to make a component draggable is to add the drag prop to a motion component.

<motion.div drag />

Axis locking

To lock dragging to a single axis, you can set the prop to "x" or "y".

<motion.div drag="x" />

Visual feedback with whileDrag

You can animate to an animation state while an element is being dragged using the whileDrag prop.

When the gesture starts, the component will animate to the state defined in whileDrag. When it ends, it will animate back to its default animate state.

This is great for creating a "lift" effect, for instance by increasing the element's scale and adding a box shadow.

<motion.div
  drag
  whileDrag={{
    scale: 1.1,
    boxShadow: "0px 10px 20px rgba(0,0,0,0.2)"
  }}
/>

Momentum

By default, when a user releases a draggable element, it has momentum. It will perform an inertia animation based on the velocity of the pointer, creating a realistic, physical feel.

You can disable this behaviour by setting the dragMomentum prop to false.

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

You can also customise the physics of this inertia animation with the dragTransition prop. This is useful for creating a heavier or bouncier feel.

<motion.div
  drag
  dragTransition={{
    bounceStiffness: 600,
    bounceDamping: 10
  }}
/>

Constraints

You can constrain the movement of a draggable element using the dragConstraints prop.

Pixel constraints

The simplest way to apply constraints is by passing an object of top, left, right, and bottom values, measured in pixels.

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

Ref-based constraints

For more dynamic constraints, you can pass a ref to another component. The draggable element will then be constrained to the bounding box of that element.

import { motion } from "motion/react"
import { useRef } from "react"

export function DragContainer() {
  const constraintsRef = useRef(null)

  return (
    <motion.div ref={constraintsRef} style={{ width: 300, height: 200 }}>
      <motion.div drag dragConstraints={constraintsRef} />
    </motion.div>
  )
}

Elastic constraints

By default, dragging an element beyond its constraints will "tug" with some elasticity. You can change this behavior with the dragElastic prop, which accepts a value between 0 (no movement) and 1 (full movement).

<motion.div
  drag
  dragConstraints={{ left: 0, right: 300 }}
  dragElastic={0.1}
/>

Direction locking

You can lock an element to the first axis it's dragged on by setting the dragDirectionLock prop to true.

<motion.div
  drag="x"
  dragDirectionLock
  onDirectionLock={axis => console.log(`Locked to ${axis} axis`)}
/>

Drag events

You can listen to the lifecycle of a drag gesture with a set of event listeners. These are useful for updating other parts of your UI in response to a drag.

The main events are onDragStart, onDrag, and onDragEnd.

Each callback is provided with the original PointerEvent, and an info object containing valuable data about the gesture's state:

  • point: The x and y coordinates of the pointer.

  • delta: The distance moved since the last event.

  • offset: The distance from the element's origin.

  • velocity: The current velocity of the pointer.

function onDrag(event, info) {
  console.log(info.point.x, info.point.y)
}

<motion.div drag onDrag={onDrag} />

Manual control with useDragControls

In some cases, you might want to initiate a drag from a different element, like a handle or a video scrubber. You can achieve this with the useDragControls hook.

The hook returns a set of dragControls that you can pass to the draggable element. You can then call the controls.start() method from any event to begin the gesture.

import { motion, useDragControls } from "motion/react"

export function Scrubber() {
  const dragControls = useDragControls()

  function startDrag(event) {
    // Start the drag gesture imperatively
    dragControls.start(event, { snapToCursor: true })
  }

  return (
    <>
      <div onPointerDown={startDrag} className="scrubber-track" />
      <motion.div
        drag="x"
        dragControls={dragControls}
        dragListener={false} // Disable the default drag handler
        className="scrubber-handle"
      />
    </>
  )
}

Drag animations allow users to move elements with their pointer.

Motion provides a suite of features to create app-quality drag animations with a simple API:

  • Momentum

  • Axis control

  • Elastic constraints

  • Direction locking

  • Optional imperative start/stop controls

Although browsers provide a native Drag and Drop API, it can be challenging to create a pleasant experience, with an odd "ghost image" effect. On the other hand, it also contains native dropzone functionality - which isn't yet in Motion.

In this guide we'll take a look at all Motion's drag features and how to customise them.

The drag prop

The simplest way to make a component draggable is to add the drag prop to a motion component.

<motion.div drag />

Axis locking

To lock dragging to a single axis, you can set the prop to "x" or "y".

<motion.div drag="x" />

Visual feedback with whileDrag

You can animate to an animation state while an element is being dragged using the whileDrag prop.

When the gesture starts, the component will animate to the state defined in whileDrag. When it ends, it will animate back to its default animate state.

This is great for creating a "lift" effect, for instance by increasing the element's scale and adding a box shadow.

<motion.div
  drag
  whileDrag={{
    scale: 1.1,
    boxShadow: "0px 10px 20px rgba(0,0,0,0.2)"
  }}
/>

Momentum

By default, when a user releases a draggable element, it has momentum. It will perform an inertia animation based on the velocity of the pointer, creating a realistic, physical feel.

You can disable this behaviour by setting the dragMomentum prop to false.

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

You can also customise the physics of this inertia animation with the dragTransition prop. This is useful for creating a heavier or bouncier feel.

<motion.div
  drag
  dragTransition={{
    bounceStiffness: 600,
    bounceDamping: 10
  }}
/>

Constraints

You can constrain the movement of a draggable element using the dragConstraints prop.

Pixel constraints

The simplest way to apply constraints is by passing an object of top, left, right, and bottom values, measured in pixels.

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

Ref-based constraints

For more dynamic constraints, you can pass a ref to another component. The draggable element will then be constrained to the bounding box of that element.

import { motion } from "motion/react"
import { useRef } from "react"

export function DragContainer() {
  const constraintsRef = useRef(null)

  return (
    <motion.div ref={constraintsRef} style={{ width: 300, height: 200 }}>
      <motion.div drag dragConstraints={constraintsRef} />
    </motion.div>
  )
}

Elastic constraints

By default, dragging an element beyond its constraints will "tug" with some elasticity. You can change this behavior with the dragElastic prop, which accepts a value between 0 (no movement) and 1 (full movement).

<motion.div
  drag
  dragConstraints={{ left: 0, right: 300 }}
  dragElastic={0.1}
/>

Direction locking

You can lock an element to the first axis it's dragged on by setting the dragDirectionLock prop to true.

<motion.div
  drag="x"
  dragDirectionLock
  onDirectionLock={axis => console.log(`Locked to ${axis} axis`)}
/>

Drag events

You can listen to the lifecycle of a drag gesture with a set of event listeners. These are useful for updating other parts of your UI in response to a drag.

The main events are onDragStart, onDrag, and onDragEnd.

Each callback is provided with the original PointerEvent, and an info object containing valuable data about the gesture's state:

  • point: The x and y coordinates of the pointer.

  • delta: The distance moved since the last event.

  • offset: The distance from the element's origin.

  • velocity: The current velocity of the pointer.

function onDrag(event, info) {
  console.log(info.point.x, info.point.y)
}

<motion.div drag onDrag={onDrag} />

Manual control with useDragControls

In some cases, you might want to initiate a drag from a different element, like a handle or a video scrubber. You can achieve this with the useDragControls hook.

The hook returns a set of dragControls that you can pass to the draggable element. You can then call the controls.start() method from any event to begin the gesture.

import { motion, useDragControls } from "motion/react"

export function Scrubber() {
  const dragControls = useDragControls()

  function startDrag(event) {
    // Start the drag gesture imperatively
    dragControls.start(event, { snapToCursor: true })
  }

  return (
    <>
      <div onPointerDown={startDrag} className="scrubber-track" />
      <motion.div
        drag="x"
        dragControls={dragControls}
        dragListener={false} // Disable the default drag handler
        className="scrubber-handle"
      />
    </>
  )
}

Drag animations allow users to move elements with their pointer.

Motion provides a suite of features to create app-quality drag animations with a simple API:

  • Momentum

  • Axis control

  • Elastic constraints

  • Direction locking

  • Optional imperative start/stop controls

Although browsers provide a native Drag and Drop API, it can be challenging to create a pleasant experience, with an odd "ghost image" effect. On the other hand, it also contains native dropzone functionality - which isn't yet in Motion.

In this guide we'll take a look at all Motion's drag features and how to customise them.

The drag prop

The simplest way to make a component draggable is to add the drag prop to a motion component.

<motion.div drag />

Axis locking

To lock dragging to a single axis, you can set the prop to "x" or "y".

<motion.div drag="x" />

Visual feedback with whileDrag

You can animate to an animation state while an element is being dragged using the whileDrag prop.

When the gesture starts, the component will animate to the state defined in whileDrag. When it ends, it will animate back to its default animate state.

This is great for creating a "lift" effect, for instance by increasing the element's scale and adding a box shadow.

<motion.div
  drag
  whileDrag={{
    scale: 1.1,
    boxShadow: "0px 10px 20px rgba(0,0,0,0.2)"
  }}
/>

Momentum

By default, when a user releases a draggable element, it has momentum. It will perform an inertia animation based on the velocity of the pointer, creating a realistic, physical feel.

You can disable this behaviour by setting the dragMomentum prop to false.

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

You can also customise the physics of this inertia animation with the dragTransition prop. This is useful for creating a heavier or bouncier feel.

<motion.div
  drag
  dragTransition={{
    bounceStiffness: 600,
    bounceDamping: 10
  }}
/>

Constraints

You can constrain the movement of a draggable element using the dragConstraints prop.

Pixel constraints

The simplest way to apply constraints is by passing an object of top, left, right, and bottom values, measured in pixels.

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

Ref-based constraints

For more dynamic constraints, you can pass a ref to another component. The draggable element will then be constrained to the bounding box of that element.

import { motion } from "motion/react"
import { useRef } from "react"

export function DragContainer() {
  const constraintsRef = useRef(null)

  return (
    <motion.div ref={constraintsRef} style={{ width: 300, height: 200 }}>
      <motion.div drag dragConstraints={constraintsRef} />
    </motion.div>
  )
}

Elastic constraints

By default, dragging an element beyond its constraints will "tug" with some elasticity. You can change this behavior with the dragElastic prop, which accepts a value between 0 (no movement) and 1 (full movement).

<motion.div
  drag
  dragConstraints={{ left: 0, right: 300 }}
  dragElastic={0.1}
/>

Direction locking

You can lock an element to the first axis it's dragged on by setting the dragDirectionLock prop to true.

<motion.div
  drag="x"
  dragDirectionLock
  onDirectionLock={axis => console.log(`Locked to ${axis} axis`)}
/>

Drag events

You can listen to the lifecycle of a drag gesture with a set of event listeners. These are useful for updating other parts of your UI in response to a drag.

The main events are onDragStart, onDrag, and onDragEnd.

Each callback is provided with the original PointerEvent, and an info object containing valuable data about the gesture's state:

  • point: The x and y coordinates of the pointer.

  • delta: The distance moved since the last event.

  • offset: The distance from the element's origin.

  • velocity: The current velocity of the pointer.

function onDrag(event, info) {
  console.log(info.point.x, info.point.y)
}

<motion.div drag onDrag={onDrag} />

Manual control with useDragControls

In some cases, you might want to initiate a drag from a different element, like a handle or a video scrubber. You can achieve this with the useDragControls hook.

The hook returns a set of dragControls that you can pass to the draggable element. You can then call the controls.start() method from any event to begin the gesture.

import { motion, useDragControls } from "motion/react"

export function Scrubber() {
  const dragControls = useDragControls()

  function startDrag(event) {
    // Start the drag gesture imperatively
    dragControls.start(event, { snapToCursor: true })
  }

  return (
    <>
      <div onPointerDown={startDrag} className="scrubber-track" />
      <motion.div
        drag="x"
        dragControls={dragControls}
        dragListener={false} // Disable the default drag handler
        className="scrubber-handle"
      />
    </>
  )
}

Related topics

Motion+

Motion+

Motion+

Level up your animations with Motion+

Unlock the full vault of 290+ Motion examples, premium APIs, private Discord and GitHub, and powerful VS Code animation editing tools.

One-time payment, lifetime updates.

Motion is supported by the best in the industry.

Stay in the loop

Subscribe for the latest news & updates.

Stay in the loop

Subscribe for the latest news & updates.