---
order: 0
title: React drop indicator
description:
  An optional package containing react components that provide a visual indication about what the
  user will achieve when the drop (eg lines)
---

import SectionMessage from '@atlaskit/section-message';
import TreeItemExample from '../../examples/constellation/tree-item';
import { TypeExample } from '../../examples/constellation/box/box-type';
import { AppearanceExample } from '../../examples/constellation/box/box-appearance';
import { EdgeExample } from '../../examples/constellation/box/box-edge';
import { GapExample } from '../../examples/constellation/box/box-gap';
import { OverlapExample } from '../../examples/constellation/box/box-overlap';
import { IndentExample } from '../../examples/constellation/box/box-indent';
import { ListItemVerticalExample } from '../../examples/constellation/list-item/list-item-vertical';
import { ListItemHorizontalExample } from '../../examples/constellation/list-item/list-item-horizontal';
import { BorderShowcaseExample } from '../../examples/constellation/border/border-showcase';
import { GroupExample } from '../../examples/constellation/group/group-list';

This package contains `react` components to display hitbox information, which is usually generated
by our optional [hitbox package](/components/pragmatic-drag-and-drop/optional-packages/hitbox). You
are also able to create your own drop indicators to render hitbox information.

These packages embody our
[design guidelines](/components/pragmatic-drag-and-drop/design-guidelines).

<SectionMessage>

You are welcome to reimplement this small optional package using your own tech stack.

This package depends on:

- [the core package](/components/pragmatic-drag-and-drop/core-package)
- [the hitbox package](/components/pragmatic-drag-and-drop/optional-packages/hitbox)
- [`react`](https://react.dev/)
- [`@atlaskit/tokens`](/components/tokens) for colors
- [`compiled`](https://compiledcssinjs.com/) for styling

[Compiled](https://compiledcssinjs.com/) is a styling solution with a tiny runtime (`~260B`) that
you can use along side your existing styling solution(s). As long as your bundler supports CSS
imports (eg `import './styles.css'`) then you shouldn't need to do anything!
[More information for if you run into any difficulties](https://community.developer.atlassian.com/t/rfc-73-migrating-our-components-to-compiled-css-in-js/85953).

</SectionMessage>

<SectionMessage title="For Atlassians" appearance='information'>

Please avoid creating your own drop indicators and use our provided ones. This helps us improve user
experience consistency and reduce bundle sizes.

</SectionMessage>

<SectionMessage title="React 19" appearance='information'>

We expect this package to work with React 19 and have included it in the supported peer dependency
range.

We do not currently test against React 19 directly, so there is a small risk of issues. If you have
any problems, please raise an issue on
[Github](https://github.com/atlassian/pragmatic-drag-and-drop) and we can look into it.

</SectionMessage>

## `position: relative`

Generally speaking, all drop indicators leverage `position:absolute` and will be positioned relative
to the closest parent with `position:relative` (or any other
[non-static position](https://developer.mozilla.org/en-US/docs/Web/CSS/position)). The exception to
this is the [group drop indicator](#group).

```tsx
const itemStyles = css({
	position: 'relative',
	padding: token('space.100'),
	backgroundColor: token('elevation.surface'),
});

export function Item({
	content,
	instruction,
}: {
	content: ReactNode;
	instruction: Instruction | null;
}) {
	return (
		<div css={itemStyles}>
			<span>{content}</span>
			{instruction && <DropIndicator edge={instruction} />}
		</div>
	);
}
```

## List item

<SectionMessage>

[`position: relative` needed on parent](#position-relative)

</SectionMessage>

A drop indicator to draw a drop indicator for list item operations (`"reorder-before"`, `"combine"`
and `"reorder-after"`). This output is commonly used in to visualize the
[list item hitbox](/components/pragmatic-drag-and-drop/optional-packages/hitbox) results.

```ts
import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/list-item';
```

This drop indicator will internally render either a [box drop indicator](#box) for
`"reorder-before"` and `"reorder-after"` operations, and a [border drop indicator](#border) for
`"combine"`.

#### Props

- `instruction`: the `Instruction` to be rendered.
  [See hitbox for details on instructions](/components/pragmatic-drag-and-drop/optional-packages/hitbox#list-item)
- `lineGap`: the gap between list items ([see box > gap](#gap))
- `lineType`: the variant of lines to be used ([see box > type](#type))

#### Vertical list

<Example Component={ListItemVerticalExample} appearance="showcase-only" />

#### Horizontal list

<Example Component={ListItemHorizontalExample} appearance="showcase-only" />

## Box

<SectionMessage>

[`position: relative` needed on parent](#position-relative)

</SectionMessage>

A drop indicator to draw a line on an `Edge` (`top`, `right`, `bottom`, `left`). This output is
commonly used in to visualize the
[box hitbox](/components/pragmatic-drag-and-drop/optional-packages/hitbox) results.

```ts
import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box';
```

If the element you want to position against has a `border`, you will need to wrap the element in
another element with `position:relative` for `gap` to work correctly.
[Information about `gap`](#gap).

```tsx
// position:relative on the parent of the element with a border
const relativeStyles = css({
	position: 'relative',
});

const itemStyles = css({
	padding: token('space.100'),
	backgroundColor: token('elevation.surface'),
	// Our item has a border
	borderWidth: token('border.width'),
	borderStyle: 'solid',
	borderColor: token('color.border'),
});

export function Item({ content, closestEdge }: { content: ReactNode; closestEdge: Edge | null }) {
	return (
		<div css={relativeStyles}>
			<div css={itemStyles}>
				<span>{content}</span>
				{closestEdge && <DropIndicator edge={closestEdge} />}
			</div>
		</div>
	);
}
```

### Edge

Drop indicators are rendered on for a provided `Edge` (`top`, `right`, `bottom`, `left`)

<Example Component={EdgeExample} appearance="showcase-only" />

In order to help people be successful with performance, we have made `edge` a _required_ prop. This
will force you to only render the indicator when it is relevant (minimizing `react` and DOM nodes)

```tsx
export function Item() {
	const ref = useRef<HTMLDivElement | null>(null);
	const [closestEdge, setClosestEdge] = useState<Edge | null>(null);

	useEffect(() => {
		const el = ref.current;
		invariant(el);
		return combine(
			draggable({
				element: el,
			}),
			dropTargetForElements({
				element: el,
				// just being simple and always using the 'bottom' edge
				// ideally this is set using our `@atlaskit/pragmatic-drag-and-drop-hitbox/box` package
				onDragStart: () => setClosestEdge('bottom'),
				onDragEnter: () => setClosestEdge('bottom'),
				onDragLeave: () => setClosestEdge(null),
				onDrop: () => setClosestEdge(null),
			}),
		);
	}, []);

	return (
		<div css={relativeStyles} ref={ref}>
			<div css={itemStyles}>item A</div>
			{/* DropIndicator is being conditionally rendered */}
			{closestEdge && <DropIndicator edge={closestEdge} />}
		</div>
	);
}
```

### Type

##### `"terminal"`

- display a terminal (circle with a whole in it) at the start of the line
- half the size of the terminal will "bleed out" of the containing element
- **first** preference for Atlassian experiences

##### `"terminal-no-bleed"`

- display a terminal (circle with a whole in it) at the start of the line
- the terminal will _not_ "bleed out" of the containing element
- this is useful in situations where the terminal cannot bleed out (such as when inside scroll
  containers with no padding)
- **second** preference for Atlassian experiences

##### `"no-terminal"`

- display a full width line with no terminal
- **third** preference for Atlassian experiences

<Example Component={TypeExample} appearance="showcase-only" />

### Appearance

The drop indicator can be a `"standard"` (blue) line or a `"warning"` (yellow) line.

<Example Component={AppearanceExample} appearance="showcase-only" />

### Gap

Often you have drop targets that are separated by a gap (eg in a list). You can provide this `gap`
to the drop indicator and the drop indicator will be rendered in the middle of the gap.

<SectionMessage>

If you want to continue to show a drop indicator when _between_ drop targets, it is also important
that you enable
[stickiness for your drop targets](https://atlassian.design/components/pragmatic-drag-and-drop/core-package/drop-targets#getissticky).

</SectionMessage>

<Example Component={GapExample} appearance="showcase-only" />

`gap` is helpful to ensure that the drop indicator stays in the same visual position when moving
from one `Edge` of a drop target to another `Edge` of another drop target when there is space
between the drop targets.

<Example Component={OverlapExample} appearance="showcase-only" />

### Indent

For some experiences, you may need to indent the drop indicator. An example of this is in a tree
interface where the drop indicator can shift based on which level the item belongs to.

<Example Component={IndentExample} appearance="showcase-only" />

## Border

<SectionMessage>

[`position: relative` needed on parent](#position-relative)

</SectionMessage>

A drop indicator which draws a border around an element. Right now, this component uses
`position:absolute` rather than being a true border in order to support `tree-item` indenting. So as
with the other drop indicators, this component will be rendered relative to the closest
`position:relative` parent.

#### Props

- `appearance`: `'default' | 'warning'`: used to control the color of the border
- `indent`: can be used to indent the border (needed for `tree-item`s).

<Example Component={BorderShowcaseExample} appearance="showcase-only" />

## Group

Our group drop indicator is helpful to indicate that you are over a group of items. It is currently
limited in the amount of control it provides.

Unlike all other drop indicators:

- The `GroupDropIndicator` does not need a `position:relative` parent (it wraps DOM elements)
- It is always rendered, and it's visibility is toggled with the `isActive` prop.
- We've found it's often helpful to make a `GroupDropIndicator` a drop target
- `GroupDropIndicator` exposes a `ref` so you can wire it up to be a drop target.

<Example Component={GroupExample} appearance="showcase-only" />

```tsx
import { GroupDropIndicator } from '@atlassian/pragmatic-drag-and-drop-react-drop-indicator/group';

function isInnerMostGroup({ location, self }: ElementDropTargetEventBasePayload): boolean {
	const [innerMost] = location.current.dropTargets.filter(
		(dropTarget) => dropTarget.data.type === 'menu-item-group',
	);
	return innerMost?.element === self.element;
}

export function MyMenuItemGroup() {
	const [isInnerMostOver, setIsInnerMostOver] = useState<boolean>(false);
	const ref = useRef<HTMLDivElement | null>(null);

	useEffect(() => {
		const element = ref.current;
		invariant(element);
		return dropTargetForElements({
			element,
			canDrop: ({ source }) => source.data.type === 'menu-item',
			getData: () => ({ type: 'menu-item-group' }),
			onDragStart: (args) => setIsInnerMostOver(isInnerMostGroup(args)),
			onDropTargetChange: (args) => setIsInnerMostOver(isInnerMostGroup(args)),
			onDrop: () => setIsInnerMostOver(false),
		});
	}, []);

	return (
		<GroupDropIndicator isActive={isInnerMostOver} ref={ref}>
			<OurLinkMenuItem />
			<OurButtonItem />
			<OurFlyoutItem />
			<OurExpandableItem />
		</GroupDropIndicator>
	);
}
```

## Tree item

<SectionMessage appearance="warning">

The usage of tree item is no longer encouraged. Please now use our more flexible
[list item](#list-item) which can also be used to power trees.

</SectionMessage>

<SectionMessage>

[`position: relative` needed on parent](#position-relative)

</SectionMessage>

A drop indicator used to visualize a
[tree item hitbox `Instruction`](/components/pragmatic-drag-and-drop/optional-packages/hitbox).

- A line is used to indicate reordering
- A border is used to indicate that an item will be made a child (`"make-child"`)
- Blocked instructions are drawn in a warning color. It was an intentional choice to provide users
  with a visual indication that an `instruction` is blocked during a drag.

```ts
import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/tree-item';
```

<Example Component={TreeItemExample} appearance="showcase-only" />
