Draggable
DnD & InteractionWrapper that makes any element draggable.
Preview
⠿ Drag me around!
Usage
example.jsx
import { Draggable } from "@/components/ui/draggable";
export default function Example() {
return <Draggable />;
}Source Code
Copy this file into components/ui/draggable.jsx in your project.
draggable.jsx
"use client";
import { forwardRef, useRef, useState, useCallback } from "react";
import { cn } from "@/lib/utils";
const Draggable = forwardRef(({ className, children, onDragEnd: onDragEndProp, ...props }, ref) => {
const [pos, setPos] = useState({ x: 0, y: 0 });
const startRef = useRef({ x: 0, y: 0, px: 0, py: 0 });
const onMouseDown = useCallback((e) => {
e.preventDefault();
startRef.current = { x: pos.x, y: pos.y, px: e.clientX, py: e.clientY };
const onMove = (e) => {
setPos({
x: startRef.current.x + e.clientX - startRef.current.px,
y: startRef.current.y + e.clientY - startRef.current.py,
});
};
const onUp = () => {
window.removeEventListener("mousemove", onMove);
window.removeEventListener("mouseup", onUp);
onDragEndProp?.(pos);
};
window.addEventListener("mousemove", onMove);
window.addEventListener("mouseup", onUp);
}, [pos, onDragEndProp]);
return (
<div ref={ref} className={cn("cursor-grab active:cursor-grabbing", className)}
style={{ transform: `translate(${pos.x}px, ${pos.y}px)` }}
onMouseDown={onMouseDown}
{...props}
>
{children}
</div>
);
});
Draggable.displayName = "Draggable";
export { Draggable };
Quick Install
Make sure you have the cn() utility set up. It requires clsx and tailwind-merge.
npm install clsx tailwind-merge