Skip to contents

Drop Target

A component that makes its element a drop zone.

Drop me
Drop here

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:

Anatomy

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:

Accepting OS file drops

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.

Accepting only cards

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:

Accepting two kinds

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:

Accepting anything

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:

Reacting to drags over a target

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:

Expanding a collapsed group on dwell

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.

Identifying the target

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:

Telling two kinds of target apart

Most targets do not need a payload. A target rendered for each row or column can use that value directly in its handlers:

A target that already knows what it is

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:

A drop that means a time

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:

A day column of 15-minute slots

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:

A step count derived from the drag

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:

Moving an event keeps its grab point under the pointer

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.

A named drop zone

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.

Chart
Canvas
Frame
Drop into frame

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:

Rejecting every drop inside a full column

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:

Highlight the innermost target

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:

Highlight every matching target

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:

A target that renders no drag-over feedback

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:

One element, both roles

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
Description

Predicate for whether this target should be considered a candidate for the current drag. Runs after accept.

Return false to 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 inside onDrop, 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 has data-rejected. Use this for container rules such as a capacity limit. Returning false would allow an item inside the container to receive the drop.

Type
getPayloadfunction
Description

Resolves payload data from the current drag context.

Type
kindUnion
Name
Description

The target kind created with Draggable.createKind. It is available as self.kind and on entries in location.dropTargets. Use the kind’s matches method to distinguish target kinds and narrow their payload types. Its payload type must match this target’s payload.

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 use onDragEnter for enter-only side effects.

Type
onDragEnterfunction
Description

Event handler called when this target enters the active stack.

Type
onDragLeavefunction
Description

Event handler called when this target leaves the active stack, because the pointer moved away or the drag ended. eventDetails.reason identifies whether the pointer or keyboard left the target, or the drag ended.

Type
onDragStartfunction
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 onDragStart to 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’s onDragEnd.

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 onDragEnter and onDragLeave for this target’s own enter and leave.

Type
payloadTLocalData
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. Return undefined to skip snapping.

This differs from snapToGrid, which snaps the drag position for every target. snap changes only the value reported by this target.

Type
trackDragOverbooleantrue
Description

Whether to update drag-over state and its data attributes. Set to false when the target renders no drag-over feedback; drag callbacks still fire.

Type
Default
true
disabledbooleanfalse
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
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 ReactElement or 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 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.

DropTarget.Root.PropsHide

Re-Export of Root props as DropTargetRootProps

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 label on a draggable or drop target instead.

Type
idsymbol
Name
Description

The kind’s runtime identity. createKind creates a fresh symbol for each call; createGlobalKind interns it on the namespaced key.

Type
matchesmatches
Description

Whether this drag source is of this kind, narrowing its payload to TPayload.

Type