Drop Target
A component that makes its element a drop zone.
DropTarget marks where a drag can be released. It pairs with Draggable, which defines what can be picked up. See the drag and drop overview for examples and the collections guide for reorderable lists and boards.
Anatomy
Import the component and render it around the drop zone:
DropTarget.Root handles Base UI drags, not native HTML5 or operating-system drags. To accept native data such as desktop files, pass native handlers through render:
Choose which items to accept
Pass the accepted kind to accept. The target ignores other kinds, but an ancestor target can still accept them. The kind also determines the type of source.payload in drop handlers.
Pass an array to take several kinds. source.payload is then the union of their payloads, and each kind’s matches narrows it back down:
accept is required because every registration joins the same page-wide drag manager. A target without accept would accept every source on the page, including sources from unrelated drag-and-drop interactions.
Use DropTarget.anyKind for a target that intentionally accepts every drag, such as a trash zone or debug overlay. Its source.payload is unknown until a specific kind’s matches method narrows it:
For monitors and auto-scroll containers, accept is optional. Neither one resolves a drop target, so they can observe every drag by default. A monitor also receives onDrop when a drag completes on a target.
accept defines the supported kinds. Add canDrop to make a decision from the current drag source on every resolution. Use disabled to turn off the target regardless of the source.
Drop events
A target’s event handlers receive the drag source, the target as self, and the location history:
onDropTargetChange also fires on stack changes that don’t affect this target’s own membership (a nested descendant entered or left), and onDragStart fires only when a drag begins with this target already under the pointer. To observe every drop regardless of target depth, use a source’s onDrop or useDragMonitor.
Use onDragEnter and onDragLeave to implement hover intent. For example, start a timer in onDragEnter to expand a collapsed group after a delay, and clear it in onDragLeave:
Every handler receives the event payload first. Its second argument, eventDetails, contains the event reason and native event. For onDragLeave, the reason distinguishes leaving with the pointer or keyboard from ending the drag.
Attach data to a target
payload attaches data to the target and is available as self.payload in its callbacks. Use getPayload when the data depends on the current drag. It runs on each resolution with the same context as canDrop. TypeScript infers source.payload from accept and self.payload from the target’s payload.
A target can also declare its own kind. It is available as self.kind and on entries in location.dropTargets. Use it when a shared handler must distinguish several target kinds that accept the same drag. The kind’s matches method narrows the record type:
Most targets do not need a payload. A target rendered for each row or column can use that value directly in its handlers:
Use payload when a monitor or another target must read the target’s identity from location.dropTargets. Those records have an unknown payload because any target on the page can appear there. When possible, read self.payload from the target’s own callbacks.
Read where in the target the pointer landed
self.getLocalPoint() returns the pointer’s position inside the target as a fraction of its box on each axis: 0 at the left or top edge, 1 at the right or bottom. Use it when the drop resolves to a value spread across the target, such as a time on a day column, rather than to the target itself:
The first call measures the target. Later calls on the same record reuse that measurement. Records are rebuilt on every move.
Every record has this method. Entries in location.dropTargets measure against their own bounding boxes, so nested targets report different fractions for the same pointer.
The value is not clamped, because an ancestor in the stack can have the pointer outside its own box. Clamp it where your domain requires it. A target with no extent, including one detached since the drag began, reports { x: 0, y: 0 }.
Snap to steps
When the target represents fixed intervals such as 15-minute slots, weekday columns, or percentage stops, declare snap and read getSnappedLocalPoint(). It returns the same fraction, rounded to equal steps on each axis and clamped from 0 to 1. Rounding is symmetric around each step midpoint:
Step counts divide the target’s bounding box rather than using pixels. Base UI measures the box when resolving the drag, so a viewport-sized column can declare its slots without knowing its dimensions, including during server rendering. Put the target on the grid element so headers and toolbars are excluded from the stepped area.
Pass a callback when the step count depends on the drag. It receives the same source, element, and input context as canDrop. It runs on the first snapped read for each resolution and may return undefined to skip snapping:
The callback also suits a count read from live state at drag time, such as a user-adjustable slot duration read through a ref. For plain render-state a static value works too: parameters are re-read on every resolution, so a re-render with a different snap needs no callback.
When moving an element, the committed value usually represents where the element lands rather than the pointer position. Pass { anchor: 'source' } to snap the dragged element’s leading edges while preserving the pointer’s grab offset:
snap changes only the value reported by this target. To snap the preview and hit-test position for every target, use the snapToGrid modifier.
Name a target for screen readers
Pass label to name the target in the default keyboard-drag announcements. Moving over a labeled target announces “Water the plants on Done”, and dropping announces “Dropped Water the plants on Done.” With nested targets, the announcement names the innermost labeled target. Without a label, moves are silent and the drop announcement omits the destination.
Nested drop targets
Drop targets can be nested. The innermost target that accepts the drag handles it, so nested zones behave predictably. Return false from canDrop on an inner target to let an outer one claim the drop instead, or set disabled to take a target out of play entirely, which likewise lets drags fall through to its ancestors.
Returning false means “skip this target,” not “block this area.” The engine keeps looking through the nested targets under the pointer, so another target can still receive the drop. This is useful when an inner target wants to defer to its parent.
Use 'reject' when a rule must block the drop everywhere within a target. For example, if a full column returns false, one of the cards inside it could still accept the drop. Returning 'reject' from the column prevents the drop from resolving to that card, the column, or an ancestor. While the pointer is over the rejecting column, data-rejected is present so you can show that the column is full:
Track the drag-over state
data-drag-over is present while an accepted drag is over a target or one of its descendants. With nested targets, every accepted target under the pointer receives data-drag-over; only the innermost one, which would handle the drop, also receives data-drag-over-innermost:
data-accepting is present on every enabled, tracked target whose accept prop matches the active drag, from pickup until the drag ends. Unlike data-drag-over, it does not depend on the pointer’s location. Use it to reveal all targets configured for that kind as soon as dragging starts:
Among enabled targets with trackDragOver enabled, data-accepting is based only on the target’s accept prop. A target can therefore have data-accepting and still refuse the drop when canDrop runs. Use the attribute to show which targets support the dragged item; use canDrop for additional rules that are checked when the drag moves over a target. If canDrop returns 'reject', style that target with data-rejected.
If a target does not use these drag-feedback attributes, set trackDragOver={false}. The target still participates in drop resolution and its callbacks still fire, but data-drag-over, data-drag-over-innermost, data-accepting, and data-rejected are not added. This also avoids re-rendering the target as the drag moves, which can be useful when many rows in a list are drop targets:
Both a source and a target
A sortable list item is picked up as well as dropped on. Pass a DropTarget.Root to the Draggable.Root’s render prop and both roles land on one element:
The sortable list example builds a full reorderable list on top of this.
API reference
The dragged item’s payload type and this target’s payload flow through every event: a
target with an accept and a payload hands both to onDrop, canDrop, and the
rest. The generated tables below render those signatures at the default types
(unknown/undefined, or any where both overloads are shown), because the
reference is extracted without concrete type arguments. Read them as “what this
target’s accept and payload resolve to”.
Root
labelstring—
- Name
- Description
Human-readable name of this drop target, used by the default screen-reader announcements for keyboard drags to name where the item is and where it landed.
- Type
accept*Union—
- Name
- Type
canDropfunction—
- Name
- Description
Predicate for whether this target should be considered a candidate for the current drag. Runs after
accept.Return
falseto skip this target for the current resolution. Base UI continues through its ancestors, so a parent target can receive the drop. This differs from ignoring the drop insideonDrop, which does not give a parent target a chance.Return
'reject'to block every drop at this position. Descendants, this target, and ancestors cannot receive the drop. While the drag is over the target, it hasdata-rejected. Use this for container rules such as a capacity limit. Returningfalsewould allow an item inside the container to receive the drop.- Type
getPayloadfunction—
- Name
- Description
Resolves payload data from the current drag context.
- Type
kindUnion—
- Name
- Description
The target kind created with
Draggable.createKind. It is available asself.kindand on entries inlocation.dropTargets. Use the kind’smatchesmethod to distinguish target kinds and narrow their payload types. Its payload type must match this target’spayload.Distinct from
accept, which declares the source kinds this target takes.- Type
onDragfunction—
- Name
- Description
Event handler called on the frame this target enters the active stack, right after
onDragEnter, and on every rAF tick the pointer moves while the target remains in the stack. Put hover-tracking work here and useonDragEnterfor enter-only side effects.- Type
onDragEnterfunction—
- Name
- Description
Event handler called when this target enters the active stack.
- Type
onDragLeavefunction—
- Name
- Description
Event handler called when this target leaves the active stack, because the pointer moved away or the drag ended.
eventDetails.reasonidentifies whether the pointer or keyboard left the target, or the drag ended.- Type
onDragStartfunction—
- Name
- Description
Event handler called when a matching drag starts while this target is already under the pointer. It does not fire for drags that start elsewhere; use a monitor’s
onDragStartto observe every drag.- Type
onDropfunction—
- Name
- Description
Event handler called on the innermost active drop target only, when the user releases the drag over it. Ancestor targets in the same stack do not receive
onDrop, and it never fires on a cancel. To observe every drag end regardless of target depth or cancellation, use the source’s or a monitor’sonDragEnd.- Type
onDropTargetChangefunction—
- Description
Event handler called when the active drop targets change, including changes that don’t affect this target’s own membership, such as a nested descendant entering or leaving while this ancestor stays in the stack. Use
onDragEnterandonDragLeavefor this target’s own enter and leave.- Type
payloadTLocalData—
- Name
- Description
Static payload data. Function values are preserved without being invoked.
- Type
snapUnion—
- Name
- Description
Divides the target’s border box into equal steps for
getSnappedLocalPoint(). For example,{ y: 96 }creates 15-minute slots in a day column, and{ x: 7, y: 6 }creates a month grid.Step counts do not depend on the target’s pixel size. Base UI measures the target when resolving a drag. Pass a static value or a callback that receives the same context as
canDrop. The callback runs on the first snapped read for each resolution. Returnundefinedto skip snapping.This differs from
snapToGrid, which snaps the drag position for every target.snapchanges only the value reported by this target.- Type
trackDragOverbooleantrue
- Name
- Description
Whether to update drag-over state and its data attributes. Set to
falsewhen the target renders no drag-over feedback; drag callbacks still fire.- Type
- Default
true
disabledbooleanfalse
- Name
- Description
Whether the drop target should ignore user interaction. A disabled target is skipped by target resolution as if it weren’t registered, so drags fall through to ancestor targets. A hovered target disabled mid-drag leaves the active stack, with its
onDragLeave, on the next resolution.- Type
- Default
false
classNamestring | function—
- Name
- Description
CSS class applied to the element, or a function that returns a class based on the component’s state.
- Type
styleReact.CSSProperties | function—
- Name
- Description
Style applied to the element, or a function that returns a style object based on the component’s state.
- Type
renderReactElement | function—
- Name
- Description
Allows you to replace the component’s HTML element with a different tag, or compose it with another component.
Accepts a
ReactElementor a function that returns the element to render.- Type
data-disabled
Present while the drop target is disabled.
data-accepting
Present while a drag this target accepts is active, regardless of pointer
position. Use it to highlight every compatible drop target.
Absent when trackDragOver is false.
data-drag-over
Present while a matching drag source is over the target or a nested descendant.
Absent when trackDragOver is false.
data-drag-over-innermost
Present while the target is the innermost one under the source.
Absent when trackDragOver is false.
data-drop-target
Present while the element is registered as a drop target. Base UI also uses it to resolve targets during hit testing.
data-rejected
Present while canDrop returns 'reject' for the current position. Use it
to display feedback such as a full column. Absent when trackDragOver is
false.
Attribute | Description | |
|---|---|---|
data-disabled | Present while the drop target is disabled. | |
data-accepting | Present while a drag this target accepts is active, regardless of pointer
position. Use it to highlight every compatible drop target.
Absent when | |
data-drag-over | Present while a matching drag source is over the target or a nested descendant.
Absent when | |
data-drag-over-innermost | Present while the target is the innermost one under the source.
Absent when | |
data-drop-target | Present while the element is registered as a drop target. Base UI also uses it to resolve targets during hit testing. | |
data-rejected | Present while | |
DropTarget.Root.StateHide
DropTarget.Root.PropsWithPayloadHide
createKind
Alias of Draggable.createKind, provided on the DropTarget namespace so an integration that only renders targets does not need to import the draggable entry point.
createGlobalKind
Creates a globally interned drag kind for integrations where independently evaluated bundles must match without sharing the same kind value.
The key is the runtime identity, so every call with the same key matches, including
calls made by another copy of the bundle. It must be namespaced (for example,
'myapp/card') because using the same key with incompatible payload types bypasses
TypeScript and causes the integrations to exchange the wrong payload at runtime.
Prefer createKind when the kind value can be shared directly.
Parameters
key*string—
- Name
- Description
A namespaced global key such as
'myapp/card'.- Type
Return value
DragKind
namestring
- Name
- Description
The name or global key used to create this kind. This is not an accessible name. Use
labelon a draggable or drop target instead.- Type
idsymbol
- Name
- Description
The kind’s runtime identity.
createKindcreates a fresh symbol for each call;createGlobalKindinterns it on the namespaced key.- Type
matchesmatches
- Name
- Description
Whether this drag source is of this kind, narrowing its
payloadtoTPayload.- Type