Skip to content

GSAP Draggable — Interactive Drag-and-Drop Elements

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about GSAP Draggable. We cover key concepts, practical examples, and best practices to help you master this topic.

GSAP Draggable enables mouse and touch-based dragging, throwing, snapping, and sorting for DOM elements with physics-based momentum.

What You'll Learn

By the end of this guide, you will create draggable elements with bounds and inertia, add throw physics for momentum scrolling, use snap points for grid alignment, build sortable drag-and-drop lists, handle drag events, and integrate with ScrollTrigger.

Basic Draggable

Draggable.create('.drag-box', {
    type: 'x,y',
    bounds: '.container',
    inertia: true
});

Snap to Grid

Draggable.create('.grid-box', {
    type: 'x,y',
    snap: {
        x: function(endValue) {
            return Math.round(endValue / 50) * 50;
        },
        y: function(endValue) {
            return Math.round(endValue / 50) * 50;
        }
    }
});

Drag Events

Draggable.create('.drag-box', {
    type: 'rotation',
    onDragStart: function() {
        this.target.style.backgroundColor = '#ff6b35';
    },
    onDragEnd: function() {
        this.target.style.backgroundColor = '#4ecdc4';
        console.log('Final rotation:', this.rotation);
    }
});

Sortable List

Draggable.create('.list-item', {
    type: 'y',
    bounds: '.list-container',
    onDrag: function() {
        var items = document.querySelectorAll('.list-item');
        items.forEach(function(item, i) {
            var y = item._gsTransform.y;
            var index = Math.round(Math.abs(y) / 60);
            if (index !== parseInt(item.dataset.index)) {
                item.dataset.index = index;
            }
        });
    }
});

Common Mistakes

1. Not Setting Bounds

Without bounds, elements can be dragged off-screen. Always set bounds to a container.

2. Type Mismatch

Using type: 'x,y' on an element with rotation transforms can cause unexpected behavior.

3. Inertia Without Throw

For momentum scrolling after release, set inertia: true. Without it, dragging stops immediately on release.

4. Snap Function Not Returning Value

The snap function must return a value. Returning undefined breaks snapping.

5. Touch Event Interference

Draggable handles touch by default. If other touch handlers conflict, use Draggable.zIndexBoost.

Practice Questions

Q1: What types of drag are supported? A: 'x', 'y', 'x,y', 'rotation', 'scroll', and 'scrollTop'.

Q2: How do you add momentum after release? A: Set inertia: true in the Draggable config.

Q3: How do you restrict drag to a container? A: Set bounds: '.container' or bounds: { minX: 0, maxX: 300 }.

Q4: What event fires when drag starts? A: onDragStart fires when the user begins dragging.

Q5: How do you get the current position? A: Access this.x and this.y inside event callbacks, or this.target._gsTransform.

Challenge: Build a puzzle where pieces snap to a grid. When all pieces are in correct positions, trigger a completion animation.

FAQ

Does Draggable work on mobile?

Yes. Draggable handles touch events natively.

Can I limit drag to one axis?

Yes. Use type: 'x' for horizontal only, type: 'y' for vertical only.

How do I make an element only draggable in certain conditions?

Use the onPress event and return false to prevent drag.

Can I combine Draggable with ScrollTrigger?

Yes. Use Draggable for a timeline scrubber control.

How do I prevent text selection during drag?

Add CSS: .dragging { user-select: none; }

Try It Yourself

Create a draggable box with snap.

<!DOCTYPE html>
<html>
<head>
    <title>GSAP Draggable Demo</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12/gsap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12/Draggable.min.js"></script>
    <style>
        .container { width: 400px; height: 300px; border: 2px dashed #ccc; border-radius: 8px; position: relative; }
        .drag-box { width: 60px; height: 60px; background: #4ecdc4; border-radius: 8px; cursor: grab; position: absolute; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; }
        .drag-box:active { cursor: grabbing; }
        .drag-box:nth-child(2) { background: #ff6b35; left: 100px; }
        .drag-box:nth-child(3) { background: #2a9d8f; left: 200px; }
    </style>
</head>
<body style="font-family:sans-serif;padding:20px;">
<h2>Draggable with Snap</h2>
<button onclick="resetPositions()">Reset</button>
<div class="container" id="container">
    <div class="drag-box" id="box1">1</div>
    <div class="drag-box" id="box2">2</div>
    <div class="drag-box" id="box3">3</div>
</div>
<script>
Draggable.create('.drag-box', {
    type: 'x,y',
    bounds: '#container',
    inertia: true,
    snap: {
        x: function(v) { return Math.round(v / 50) * 50; },
        y: function(v) { return Math.round(v / 50) * 50; }
    }
});

function resetPositions() {
    gsap.set('#box1', { x: 0, y: 0 });
    gsap.set('#box2', { x: 100, y: 0 });
    gsap.set('#box3', { x: 200, y: 0 });
}
</script>
</body>
</html>

What's Next

Animate text content.

Text Plugin — Text animation plugin. CSS Plugin — CSS properties plugin.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro