Tips & Tricks

How to Implement Drag and Drop with Claude Code

Learn how to implement drag and drop using Claude Code. Includes practical code examples and step-by-step guidance.

Drag-and-Drop Implementation Patterns

Drag and drop is a key feature for building intuitive UIs. It’s used everywhere: reordering lists, kanban boards, file uploads, and more. With Claude Code, you can efficiently build accessible and robust drag-and-drop experiences.

A Basic Custom Hook

> Create a React custom hook that uses the HTML Drag and Drop API.
> Support reordering a list.
import { useState, useCallback, DragEvent } from 'react';

function useSortable<T extends { id: string }>(initialItems: T[]) {
  const [items, setItems] = useState(initialItems);
  const [draggedId, setDraggedId] = useState<string | null>(null);

  const handleDragStart = useCallback((e: DragEvent, id: string) => {
    setDraggedId(id);
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', id);
  }, []);

  const handleDragOver = useCallback((e: DragEvent, targetId: string) => {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';

    if (draggedId === null || draggedId === targetId) return;

    setItems((prev) => {
      const dragIndex = prev.findIndex((item) => item.id === draggedId);
      const targetIndex = prev.findIndex((item) => item.id === targetId);
      const newItems = [...prev];
      const [removed] = newItems.splice(dragIndex, 1);
      newItems.splice(targetIndex, 0, removed);
      return newItems;
    });
  }, [draggedId]);

  const handleDragEnd = useCallback(() => {
    setDraggedId(null);
  }, []);

  return { items, draggedId, handleDragStart, handleDragOver, handleDragEnd, setItems };
}

A Sortable List Component

function SortableList({ initialTasks }: { initialTasks: Task[] }) {
  const { items, draggedId, handleDragStart, handleDragOver, handleDragEnd } =
    useSortable(initialTasks);

  return (
    <ul role="listbox" aria-label="Task list (sortable)">
      {items.map((task) => (
        <li
          key={task.id}
          draggable
          role="option"
          aria-grabbed={draggedId === task.id}
          onDragStart={(e) => handleDragStart(e, task.id)}
          onDragOver={(e) => handleDragOver(e, task.id)}
          onDragEnd={handleDragEnd}
          className={`p-3 mb-2 rounded border cursor-grab ${
            draggedId === task.id ? 'opacity-50 border-blue-400' : 'border-gray-200'
          }`}
        >
          <span>{task.title}</span>
        </li>
      ))}
    </ul>
  );
}

File Drop Zone

> Create a component that uploads files via drag and drop.
> Include a preview and validation.
function FileDropZone({ onFiles, accept, maxSize = 5 * 1024 * 1024 }: DropZoneProps) {
  const [isDragging, setIsDragging] = useState(false);
  const [errors, setErrors] = useState<string[]>([]);

  const validateFiles = (files: File[]): File[] => {
    const errs: string[] = [];
    const valid = files.filter((file) => {
      if (accept && !accept.includes(file.type)) {
        errs.push(`${file.name}: Unsupported file type`);
        return false;
      }
      if (file.size > maxSize) {
        errs.push(`${file.name}: Exceeds the maximum file size`);
        return false;
      }
      return true;
    });
    setErrors(errs);
    return valid;
  };

  const handleDrop = (e: DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    setIsDragging(false);
    const files = Array.from(e.dataTransfer.files);
    const valid = validateFiles(files);
    if (valid.length > 0) onFiles(valid);
  };

  return (
    <div
      onDragOver={(e) => { e.preventDefault(); setIsDragging(true); }}
      onDragLeave={() => setIsDragging(false)}
      onDrop={handleDrop}
      className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors ${
        isDragging ? 'border-blue-500 bg-blue-50' : 'border-gray-300'
      }`}
    >
      <p>Drop files here, or click to select</p>
      {errors.map((err, i) => (
        <p key={i} className="text-red-500 text-sm mt-1">{err}</p>
      ))}
    </div>
  );
}

Keyboard Operation Support

For accessibility, support keyboard operation too.

const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
  if (e.key === ' ' || e.key === 'Enter') {
    e.preventDefault();
    toggleGrabbed(index);
  }
  if (e.key === 'ArrowUp' && isGrabbed) {
    e.preventDefault();
    moveItem(index, index - 1);
  }
  if (e.key === 'ArrowDown' && isGrabbed) {
    e.preventDefault();
    moveItem(index, index + 1);
  }
};

Summary

With Claude Code, you can efficiently build a wide variety of drag-and-drop experiences, from list reordering to file uploads. For more on file upload, see file upload implementation, and for accessibility, see the accessibility guide.

For the HTML Drag and Drop API spec, see the MDN Web Docs.

#Claude Code #drag and drop #React #UI #interaction

Level up your Claude Code workflow

50 battle-tested prompt templates you can copy-paste into Claude Code right now.

Free

Free PDF: Claude Code Cheatsheet in 5 Minutes

Key commands, shortcuts, and prompt examples on a single printable page.

Download PDF
M

About the Author

Masa

Engineer obsessed with Claude Code. Runs claudecode-lab.com, a 10-language tech media with 2,000+ pages.