30% offEnding soon
useDropAreaLoading saved progress…

useDropArea

useDropArea turns any element into a drop zone — a region that accepts files, text, or links dragged in from outside the page and reports whether a drag is currently hovering it. You have used one every time you dragged a photo onto a chat box or a file onto an upload area. Native HTML5 drag-and-drop has two gotchas that make a naive drop zone silently fail, and this hook exists to get them right. You will implement it in the shape popularized by react-use: call it, get back a bond of event handlers to spread on your element plus a small state object.

Signature

function useDropArea(options?: {
  onFiles?: (files: File[], event: DragEvent) => void; // dropped files
  onText?: (text: string, event: DragEvent) => void;   // dropped plain text
  onUri?: (uri: string, event: DragEvent) => void;     // a dropped link
}): [
  bond: {                        // spread onto the drop element
    onDragOver: (e) => void;
    onDragEnter: (e) => void;
    onDragLeave: (e) => void;
    onDrop: (e) => void;
  },
  state: { over: boolean },      // true while a drag is over the element
];

Examples

function Uploader() {
  const [bond, { over }] = useDropArea({
    onFiles: (files) => console.log(files.map((f) => f.name)),
    onUri: (url) => console.log('link:', url),
  });
  return (
    <div {...bond} className={over ? 'zone hovering' : 'zone'}>
      Drop files here
    </div>
  );
}
// What each drop routes to:
//   drag 2 files from the desktop      -> onFiles([File, File], event)
//   drag a link from another browser   -> onUri('https://example.com', event)
//   drag a selection of text           -> onText('the selected words', event)
//   drag over a child icon inside zone -> over stays true (no flicker)

Notes

  • preventDefault, or nothing works — a dragover must call event.preventDefault() or the browser rejects the drop and onDrop never fires. This is the first thing to get right.
  • over must not flickerdragleave fires when the cursor crosses onto a child of the zone, so a plain boolean flips off mid-drag. Keep over steady while the drag is anywhere inside.
  • Dispatch by what was dropped — read event.dataTransfer and call the matching callback: files from dataTransfer.files, a link from getData('text/uri-list'), text from getData('text'). Decide the priority when more than one is present.
  • You do not render anything — the hook returns handlers to spread; the caller owns the element and decides how over looks.
  • Callbacks may be inline — a caller can pass a fresh onFiles every render. A drop should call the latest one without re-wiring the handlers each render.
  • Test environment — jsdom has no real DataTransfer; drive drops with a mock object exposing files, types, and getData(type).