Home / lwc / lwc-drag-and-drop
lwc-drag-and-drop
HTML5 drag-and-drop API in LWC — draggable="true", dataTransfer.setData / getData, ondragover preventDefault, drop zones, visual hover state, accessibility (keyboard alternatives, aria-grabbed deprecation), and shadow DOM event leakage. NOT for uploading a file the user dropped on the component — use lwc/file-upload-patterns.
Open the skill folder on GitHub
LWC Drag and Drop
The HTML5 drag-and-drop API is the only built-in drag mechanism that works in LWC without a third-party library. The shape is fixed: source elements get draggable="true", the source fires dragstart (where the developer stashes data on event.dataTransfer), the target fires dragover (which must call event.preventDefault() to be a valid drop target) and drop (where the developer reads the data and acts).
The mistakes are repeatable. The ondragover handler forgets event.preventDefault() and the drop never fires. Or dataTransfer.setData('text', id) works in Chrome and not Firefox because Firefox requires a non-empty data type. Or the developer tries to read dataTransfer.getData in dragenter (it returns empty in dragenter and dragover for security reasons, only populated in drop). And the accessibility story is grim: HTML5 drag-and-drop has no native keyboard equivalent, so a keyboard-only user cannot use a drag-only UI at all.
In Salesforce, the Lightning Locker / Lightning Web Security sandboxing imposes one additional constraint: the dataTransfer.types and dataTransfer.items lookup may be filtered. Use setData(type, value) + getData(type) for the type names you control; expect them to round-trip exactly.
Recommended Workflow
- Decide whether HTML5 drag-and-drop is the right primitive.
For sortable lists with handles, a third-party library (Sortable.js wrapped in an LWC) is more accessible and more ergonomic. For drag-between-columns kanban, HTML5 native is reasonable. For file uploads, use a file-input or the
lightning-file-uploadcomponent. - Stash a stable identifier in
dataTransfer. Set the datain
dragstartwith a custom MIME-style type (application/x-acme-card) and a JSON-encoded payload. Read it indrop. - Always
preventDefaultondragover. This is what makesthe element a valid drop target. Forgotten 90% of the time on first drafts.
- Track the drag state in JS, not via dataTransfer. Maintain
a
@track draggedIdfield.dataTransfer.getDatareturns empty duringdragoverfor security; you cannot read your own data mid-drag. - Provide a keyboard alternative. Up/down arrow keys when
the item is focused, plus a "Move to..." button that opens a menu of valid destinations. Without this, the component fails WCAG and is unusable for keyboard-only users.
- Use
dragenter/dragleaveto drive the visual hover state.dragoverfires every ~50ms during the drag and is too noisy for class toggling. Track anover-targetboolean from the enter/leave pair. - Clean up state on
dragendregardless of drop success. Theuser can drop outside any target or hit Escape;
dragendfires in either case. Reset the visual state and cleardraggedId.
What This Skill Does Not Cover
- File-drop upload — see
lwc/lwc-file-upload-dropandlightning-file-upload. - Sortable lists with a third-party library — see
lwc/lwc-sortablejs-wrapper. - Aura drag-and-drop — Aura is deprecated for new development.
- Touch drag (mobile) — HTML5 drag-and-drop has limited touch
support; use a touch-aware library.