<Droppable />
components can be dropped on by a <Draggable />
. They also contain <Draggable />
s. A <Draggable />
must be contained within a <Droppable />
.
import { Droppable } from 'react-beautiful-dnd';
<Droppable droppableId="droppable-1" type="PERSON">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }}
{...provided.droppableProps}
>
<h2>I am a droppable!</h2>
{provided.placeholder}
</div>
)}
</Droppable>;
import type { Node } from 'react';
type Props = {|
// required
droppableId: DroppableId,
// optional
type?: TypeId,
mode?: DroppableMode,
isDropDisabled?: boolean,
isCombineEnabled?: boolean,
direction?: Direction,
ignoreContainerClipping?: boolean,
renderClone?: DraggableChildrenFn,
getContainerForClone?: () => HTMLElement,
children: (DroppableProvided, DroppableStateSnapshot) => Node,
|};
type DroppableMode = 'standard' | 'virtual';
type Direction = 'horizontal' | 'vertical';
react-beautiful-dnd
will throw an error if a required prop is not provided
droppableId
: A requiredDroppableId(string)
. See our identifiers guide for more information.
type
: ATypeId(string)
that can be used to simply accept only the specified class of<Draggable />
.<Draggable />
s always inherit type from the<Droppable />
they are defined in. For example, if you use the typePERSON
then it will only allow<Draggable />
s of typePERSON
to be dropped on itself.<Draggable />
s of typeTASK
would not be able to be dropped on a<Droppable />
with typePERSON
. If notype
is provided, it will be set to'DEFAULT'
.isDropDisabled
: A flag to control whether or not dropping is currently allowed on the<Droppable />
. You can use this to implement your own conditional dropping logic. It will default tofalse
.isCombineEnabled
: A flag to control whether or not all theDraggables
in the list will be able to be combined with. It will default tofalse
.direction
: The direction in which items flow in this droppable. Options arevertical
(default) andhorizontal
.ignoreContainerClipping
: When a<Droppable />
is inside a scrollable container its area is constrained so that you can only drop on the part of the<Droppable />
that you can see. Setting this prop opts out of this behavior, allowing you to drop anywhere on a<Droppable />
even if it's visually hidden by a scrollable parent. The default behavior is suitable for most cases so odds are you'll never need to use this prop, but it can be useful if you've got very long<Draggable />
s inside a short scroll container. Keep in mind that it might cause some unexpected behavior if you have multiple<Droppable />
s inside scroll containers on the same page.mode
:standard
(default) orvirtual
. Used to designate a list as a virtual list. See our virtual lists patternrenderClone
: used to render a clone (replacement) of the dragging<Draggable />
while a drag is occurring. See our reparenting guide for usage details. A clone must be used for virtual lists. You can use a clone without using virtual listsgetContainerForClone
: a function that returns the containing element (parent element) for a clone during a drag. See our reparenting guide.
The React
children of a <Droppable />
must be a function that returns a ReactElement
.
<Droppable droppableId="droppable-1">
{(provided, snapshot) => ({
/*...*/
})}
</Droppable>
The function is provided with two arguments:
import type { Node } from 'react';
type DroppableProvided = {|
innerRef: (?HTMLElement) => void,
droppableProps: DroppableProps,
placeholder: ?Node,
|};
type DroppableProps = {|
// used for shared global styles
'data-rbd-droppable-context-id': ContextId,
// Used to lookup. Currently not used for drag and drop lifecycle
'data-rbd-droppable-id': DroppableId,
|};
provided.innerRef
: In order for the droppable to function correctly, you must bind theprovided.innerRef
to the highest possible DOM node in theReactElement
. We do this in order to avoid needing to useReactDOM
to look up your DOM node.
For more information on using
innerRef
see our usinginnerRef
guide
provided.placeholder
: This is used to create space in the<Droppable />
as needed during a drag. This space is needed when a user is dragging over a list that is not the home list. Please be sure to put the placeholder inside of the component for which you have provided the ref. We need to increase the size of the<Droppable />
itself.provided.droppableProps (DroppableProps)
: This is an Object that contains properties that need to be applied to a Droppable element. It needs to be applied to the same element that you applyprovided.innerRef
to. It currently containsdata
attributes that we use for styling and lookups.
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
Good to go
{provided.placeholder}
</div>
)}
</Droppable>
type DroppableStateSnapshot = {|
// Is the Droppable being dragged over?
isDraggingOver: boolean,
// What is the id of the draggable that is dragging over the Droppable?
draggingOverWith: ?DraggableId,
// What is the id of the draggable that is dragging from this list?
// Useful for styling the home list when not being dragged over
draggingFromThisWith: ?DraggableId,
// Whether or not the placeholder is actively being used.
// This is useful information when working with virtual lists
// (See our virtual list pattern)
isUsingPlaceholder: boolean,
|};
The children
function is also provided with a small amount of state relating to the current drag state. This can be optionally used to enhance your component. A common use case is changing the appearance of a <Droppable />
while it is being dragged over.
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }}
{...provided.droppableProps}
>
I am a droppable!
{provided.placeholder}
</div>
)}
</Droppable>
react-beautiful-dnd
supports the combining of <Draggable />
s 🤩
You can enable a combining mode for a <Droppable />
by setting isCombineEnabled
to true
on a <Droppable />
. We have created a combining guide to help you implement combining in your lists.
It is possible to change the <Draggable />
s in a <Droppable />
for a limited set of circumstances. We have created a comprehensive changes while dragging guide
<Droppable />
s can only be dropped on by<Draggable />
s who share the sametype
. This is a simple way of allowing conditional dropping. If you do not provide atype
for the<Droppable />
, then it will only accept<Draggable />
s which also have the default type.<Draggable />
s and<Droppable />
s both will have theirtypes
set to'DEFAULT'
when none is provided. There is currently no way to set multipletypes
, or atype
wildcard that will accept<Draggable />
s of multiple any types. This could be added if there is a valid use case.- Using the
isDropDisabled
prop you can conditionally allow dropping. This allows you to do arbitrarily complex conditional transitions. This will only be considered if thetype
of the<Droppable />
matches thetype
of the currently dragging<Draggable />
. - You can disable dropping on a
<Droppable />
altogether by always settingisDropDisabled
totrue
. You can do this to create a list that is never able to be dropped on, but contains<Draggable />
s. - Technically you do not need to use
type
and do all of your conditional drop logic with theisDropDisabled
function. Thetype
parameter is a convenient shortcut for a common use case.
This library supports dragging within scroll containers (DOM elements that have overflow: auto;
or overflow: scroll;
). The only supported use cases are:
- The
<Droppable />
can itself be a scroll container with no scrollable parents - The
<Droppable />
has one scrollable parent
where a scrollable parent refers to a scroll container that is not the window itself.
For more information see how we detect scroll containers guide
We currently only support a single scroll parent. We plan on adding support for nested scroll containers
It is recommended that you put a min-height
on a vertical <Droppable />
or a min-width
on a horizontal <Droppable />
. Otherwise when the <Droppable />
is empty there may not be enough of a target for <Draggable />
being dragged with touch or mouse inputs to be over the <Droppable />
.
react-beautiful-dnd
has partial support for <Droppable />
lists that use position: fixed
. When you start a drag and any list of the same type is position:fixed
then auto window scrolling will be disabled. This is because our virtual model assumes that when the page scroll changes the position of a <Droppable />
will shift too. If a manual window scroll is detected then the scroll will be aborted. Scroll container scroll is still allowed. We could improve this support, but it would just be a big effort. Please raise an issue if you would be keen to be a part of this effort ❤️
We recommend you style the home list when it is not being dragged over. This makes it easy for a user to see where an item is dragging from. You can use the snapshot.draggingFromThisWith
value for this. This will be populated in the home list.
In this example we set the background-color
of the home list to pink
when we are dragging over the list. We set the background-color
of the home list to blue
when not dragging over the home list.
const getBackgroundColor = (snapshot: DroppableStateSnapshot): string => {
// Giving isDraggingOver preference
if (snapshot.isDraggingOver) {
return 'pink';
}
// If it is the home list but not dragging over
if (snapshot.draggingFromThisWith) {
return 'blue';
}
// Otherwise use our default background
return 'white';
};
📺 This optimisation is covered in a free lesson of our getting started course
When a user drags over, or stops dragging over, a <Droppable />
we re-render the <Droppable />
with an updated DroppableStateSnapshot > isDraggingOver
value. This is useful for styling the <Droppable />
. However, by default this will cause a render of all of the children of the <Droppable />
- which might be 100's of <Draggable />
s! This can result in a noticeable frame rate drop. To avoid this problem we recommend that you create a component that is the child of a <Droppable />
whose responsibility it is to avoid rendering children if it is not required.
Here is an example of how you could do this using class
components:
import React, { Component } from 'react';
class Student extends Component<{ student: Person }> {
render() {
// Renders out a draggable student
}
}
class InnerList extends Component<{ students: Person[] }> {
// do not re-render if the students list has not changed
shouldComponentUpdate(nextProps: Props) {
if (this.props.students === nextProps.students) {
return false;
}
return true;
}
// You could also not do your own shouldComponentUpdate check and just
// extend from React.PureComponent
render() {
return this.props.students.map((student: Person) => (
<Student student={student} />
));
}
}
class Students extends Component<{ students: Person[] }> {
render() {
return (
<Droppable droppableId="list">
{(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => (
<div
ref={provided.innerRef}
style={{
backgroundColor: provided.isDragging ? 'green' : 'lightblue',
}}
{...provided.droppableProps}
>
<InnerList students={this.props.students} />
{provided.placeholder}
</div>
)}
</Droppable>
);
}
}
Here is an example of how you could do this using function
components:
import React from 'react';
function Student (props: { student: Person }) {
// Renders out a draggable student
}
// do not re-render if the students list reference has not changed
const InnerList = React.memo(function InnerList(props: students: Person[]) {
return props.students.map((student: Person) => (
<Student student={student} />
));
});
function Students(props: { students: Person[] }) {
return (
<Droppable droppableId="list">
{(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => (
<div
ref={provided.innerRef}
style={{
backgroundColor: provided.isDragging ? 'green' : 'lightblue',
}}
{...provided.droppableProps}
>
{/* only re-render if the students array reference changes */}
<InnerList students={props.students} />
{provided.placeholder}
</div>
)}
</Droppable>
);
}
By using the approach you are able to make style changes to a <Droppable />
when it is being dragged over, but you avoid re-rendering all of the children unnecessarily.
When moving into a new list, the visible Draggables
will have their render
function called directly even with this optimisation. This is because we need to move those Draggables
out of the way. The InnerList
optimisation will prevent the <Droppable />
from calling render
on the whole list from the top down. This optimisation will prevent the non-visible Draggables
from having their render function called.
Unfortunately we are unable to apply this optimisation for you. It is a byproduct of using the render-props pattern.