Drag Reorder List

Smooth drag-to-reorder list with spring physics and animated gap

animationdragreorderlistinteractive
Install dependencies
$npm install framer-motion
Preview

Source Code

"use client";

import { useState } from "react";
import { Reorder, motion } from "framer-motion";

interface DragItem {
  id: string;
  content: React.ReactNode;
}

interface DragReorderListProps {
  items: DragItem[];
  onReorder?: (items: DragItem[]) => void;
  className?: string;
}

export default function DragReorderList({
  items: initialItems,
  onReorder,
  className = "",
}: DragReorderListProps) {
  const [items, setItems] = useState(initialItems);

  const handleReorder = (newItems: DragItem[]) => {
    setItems(newItems);
    onReorder?.(newItems);
  };

  return (
    <Reorder.Group
      axis="y"
      values={items}
      onReorder={handleReorder}
      className={`flex flex-col gap-2 ${className}`}
    >
      {items.map((item) => (
        <Reorder.Item
          key={item.id}
          value={item}
          whileDrag={{
            scale: 1.03,
            boxShadow: "0 20px 40px rgba(0,0,0,0.4)",
            zIndex: 50,
          }}
          transition={{ type: "spring", stiffness: 300, damping: 25 }}
          className="flex cursor-grab items-center gap-3 rounded-xl border border-white/10 bg-gray-900 px-4 py-3 active:cursor-grabbing"
        >
          {/* Drag handle */}
          <motion.div className="flex flex-col gap-[3px] text-gray-500">
            <div className="flex gap-[3px]">
              <div className="h-1 w-1 rounded-full bg-current" />
              <div className="h-1 w-1 rounded-full bg-current" />
            </div>
            <div className="flex gap-[3px]">
              <div className="h-1 w-1 rounded-full bg-current" />
              <div className="h-1 w-1 rounded-full bg-current" />
            </div>
            <div className="flex gap-[3px]">
              <div className="h-1 w-1 rounded-full bg-current" />
              <div className="h-1 w-1 rounded-full bg-current" />
            </div>
          </motion.div>
          <div className="flex-1 text-sm text-white">{item.content}</div>
        </Reorder.Item>
      ))}
    </Reorder.Group>
  );
}