Docs/API
makeSortableTree
Makes a container a sortable tree, rendered flat with depths — nesting by a sideways drag or by hovering a node.
function makeSortableTree<T>( target: MaybeElementRef, options: SortableTreeOptions<T>,): SortableTreeReturn<T>;Makes a container a sortable tree. Render the returned items (visible nodes in draft order, indented by depth) with a row component that calls makeSortableItem. On drop, after onDrop, the moved nodes (with their subtrees) are written into options.items, unless event.preventDefault() leaves event.result to you.
const root = useTemplateRef<HTMLElement>('root');
const { items } = makeSortableTree(root, { items: files, indentation: 20, collapsed });
Each row calls makeSortableItem with id: () => node.key.
See Trees.
Loading the demo…
<script setup lang="ts">
import { DnDProvider } from '@vue-dnd-kit/core';
import FileTree from './FileTree.vue';
</script>
<template>
<DnDProvider>
<FileTree />
</DnDProvider>
</template>Options
Options of makeSortableTree. A multi-drag moves the selected nodes as one block, each with its subtree, to one place and depth (in document order); a selected node inside another selected one rides along in its subtree. That goes for the grabbed node too (as in VS Code): it stays inside the selected node it is in, which moves with its subtree, while the overlay stays on the grabbed row. canNest and maxDepth apply to the whole block.
The other options mean what they mean on makeSortableList. They take a value, a ref or a getter (overlay, overOverlay, animation, dropAnimation, overDropAnimation: a value or a ref) and are read when the tree needs them, so a change applies to the next drag at the latest. Exceptions: id and group are read once, when the tree is created; items is the model itself; functions (getKey, getChildren, setChildren, canNest, clone, createItem, the callbacks) are passed as they are.
SortableItems<T>The root nodes. A writable ref: on drop the library gives the changed nodes their new children (setChildren) and writes a new root array. A read-only source (a getter, a readonly ref, a computed): call event.preventDefault() and apply event.result yourself (as in SortableListOptions.items).
(node: T) => KeyDefault: node.id. Also keys makeSortableItem({ item }), copies and created nodes.
(node: T) => T[]The children of a node. Default: node.children.
(node: T, children: T[]) => voidHow the default write gives a node its new children. Default: node.children = children.
(node: T, children: T[]) => TA copy of node with children, for event.result: a new tree that leaves your nodes as they are (the default write still goes through setChildren). Default: { ...node } given the children through setChildren ({ ...node, children } without a setChildren). Pass it for class instances and nodes that need more than a shallow copy.
KeyDefault: useId(), stable between server and client render. Read once.
stringvalue · ref · getterWhat people call the tree: screen reader texts say it instead of its id.
unknownvalue · ref · getterAnything to tell about the tree as a drop target: events carry it as over.data while a drag is over it.
ActivationOptionsvalue · ref · getterHow a press on this tree's rows starts a drag, instead of the provider's activation (a row's own wins).
Componentvalue · refOverlay component for this tree's rows (a row's own overlay wins). A ref switched mid-drag swaps it at once.
Componentvalue · refWhat the overlay shows while a drag this tree accepts is over it (see SortableListOptions.overOverlay). Its to prop says where the drop would nest.
DropAnimationvalue · refHow the overlay lands when a drag of this tree's rows ends (see SortableListOptions.dropAnimation).
DropAnimationvalue · refHow the overlay lands when it is dropped into this tree (see SortableListOptions.overDropAnimation).
Modifier[]value · ref · getterModifiers for drags of this tree's rows, applied after the provider's modifiers (a row's own modifiers win): e.g. [restrictToVerticalAxis]. Read when the drag starts. A locked axis still nests: the depth then comes from hovering the middle of a row.
'x' | 'y'value · ref · getterLocks drags of this tree's rows to one axis (a row's own axis wins), after the modifiers. With 'y' rows still nest: the depth then comes from hovering the middle of a row. Read when the drag starts.
string | SortableGroupOptions<T>Lists and trees with the same group name exchange items. Default: a group of its own. Read once.
AcceptModevalue · ref · getterMulti-drag of items of several types: 'all' (default) or 'some' must be accepted (group.put).
numbervalue · ref · getterPixels of horizontal offset per depth level. Default 24.
TreeNestingvalue · ref · getterHow the depth is chosen: horizontal offset, hovering the middle of a row, or both (default).
SortablePlaceholdervalue · ref · getter'item' (default): the dragged node moves through the rows at its projected depth. 'indicator': the tree doesn't move; indicator (on the tree and on each row's makeSortableItem) says where the drop goes, with its depth and parentKey, and you draw the line. Dropping outside then cancels.
numbervalue · ref · getterDeepest allowed depth (roots are 0). The dragged nodes' own descendants count.
(node: T, parent: T) => booleanWhether node may go under parent; a multi-drag asks for every dragged node.
ReadonlySet<Key>value · ref · getterKeys of the nodes whose children are hidden.
(node: T) => TCreates copies (group.pull: 'clone', the copy modifier key). Required for copying. Return the copy with its whole subtree: a copied folder keeps its children. The copy and each of its descendants need keys of their own (read with getKey), as every node of a tree does.
(active: Active) => TAccepts plain draggables from outside: creates the node to insert, at the projected position and depth (canNest and maxDepth apply to it). With the keyboard, an arrow toward the tree takes the draggable in, level with the row it goes before, and the arrow keys then move the node like any other. A drag from outside the page (external) is asked twice, while it is held and at the drop (see the list's createItem); a folder node with a child per file holds several files.
ExternalAcceptvalue · ref · getterTakes drags from outside the page: files, links, text (see ExternalAccept), with createItem. Nodes of its group dragged in from another window of the app come in as themselves. A value, a ref or a getter, read live.
DropOutsidevalue · ref · getterA drop outside every target: keep the nodes where the placeholder is, cancel, or remove them. Default 'placeholder' ('cancel' with placeholder: 'indicator'). Read at the drop.
LayoutAnimationvalue · refFLIP animation of this tree; default: the provider's animation. A value or a ref (a function is a custom animation).
AutoAnimationvalue · ref · getterAs in SortableListOptions, for the visible rows: nodes added, removed or moved, and the rows a node shows or hides as it expands or collapses (collapsed).
booleanvalue · ref · getterThe tree's own size follows its rows, as a list's does (animateSize). Default true.
boolean | AutoScrollOptionsvalue · ref · getterOverrides the provider's autoScroll for this tree's scroll container. Read when the drag starts.
MaybeElementRefAs in SortableListOptions: the element that takes drops for the tree, default the tree's own.
booleanvalue · ref · gettertrue: its rows can't be dragged, and it takes no items.
( event: SortableTreeDropEvent<T>, ) => MaybePromise<BeforeDropResult>As in SortableListOptions.
(event: SortableTreeDropEvent<T>) => voidAs in SortableListOptions: before the write, which event.preventDefault() skips.
Returns
What makeSortableTree returns.
KeyThe tree's id: the id option, or the one made for it.
ComputedRef<FlatNode<T>[]>Visible nodes in draft order; the active node carries its projected depth.
ComputedRef<TreeProjection | null>Where the active node would land, null when it isn't in this tree.
ComputedRef<SortableIndicator | null>Indicator mode: where the drop goes, with its depth and parentKey; null when not here.
ComputedRef<boolean>A drag is over the tree.
ComputedRef<boolean>Something is being dragged and this tree takes it.
ComputedRef<Key | null>The row a virtualizer must keep mounted: the slot while it is in this tree (the dragged node, the selected node the grabbed one rides in, a copy, a created node), otherwise the dragged node.
ComputedRef<number>Index of activeKey in items, -1 when it isn't here.
ComputedRef<Size | null>Size of the dragged row at drag start.
() => voidAnimates layout changes the library didn't cause (e.g. a virtualizer re-measure).
Flat nodes
A visible node of a tree, as the tree's items list it: render them in order, indented by depth.
KeyThe node's key (getKey).
TYour node.
numberIts depth: roots are 0. The dragged node has its projected depth.
Key | nullKey of its parent; null for a root.
numberIndex among siblings.
booleanIt has children, shown or not.
booleanIts children are hidden (its key is in collapsed).
Projection
Where the dragged node would land in a tree.
How a tree drag chooses its depth: 'offset', by how far it moves sideways (indentation per level); 'hover', by hovering the middle of a row, which takes it as a child; 'both'.
type TreeNesting = 'offset' | 'hover' | 'both'The drop event
Drop event of a tree. index is the position among the siblings under parentKey (from: before the move; to: after it, with the node already removed from its old place). from.parentKey is null for root nodes and for items from another list. A multi-drag moves keys (each with its subtree) side by side: from is where the node leading the drag was (the grabbed one, or the selected node it rides in), to where the first of them lands. A copy (mode: 'copy') leaves the nodes where they are and inserts items, their copies; a plain draggable (mode: 'insert') the node createItem made.