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.

Live

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) => Key

Default: 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[]) => void

How the default write gives a node its new children. Default: node.children = children.

(node: T, children: T[]) => T

A 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.

Key

Default: useId(), stable between server and client render. Read once.

stringvalue · ref · getter

What people call the tree: screen reader texts say it instead of its id.

unknownvalue · ref · getter

Anything to tell about the tree as a drop target: events carry it as over.data while a drag is over it.

ActivationOptionsvalue · ref · getter

How a press on this tree's rows starts a drag, instead of the provider's activation (a row's own wins).

Componentvalue · ref

Overlay component for this tree's rows (a row's own overlay wins). A ref switched mid-drag swaps it at once.

Componentvalue · ref

What 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 · ref

How the overlay lands when a drag of this tree's rows ends (see SortableListOptions.dropAnimation).

DropAnimationvalue · ref

How the overlay lands when it is dropped into this tree (see SortableListOptions.overDropAnimation).

Modifier[]value · ref · getter

Modifiers 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 · getter

Locks 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 · getter

Multi-drag of items of several types: 'all' (default) or 'some' must be accepted (group.put).

numbervalue · ref · getter

Pixels of horizontal offset per depth level. Default 24.

TreeNestingvalue · ref · getter

How 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 · getter

Deepest allowed depth (roots are 0). The dragged nodes' own descendants count.

(node: T, parent: T) => boolean

Whether node may go under parent; a multi-drag asks for every dragged node.

ReadonlySet<Key>value · ref · getter

Keys of the nodes whose children are hidden.

(node: T) => T

Creates 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) => T

Accepts 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 · getter

Takes 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 · getter

A 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 · ref

FLIP animation of this tree; default: the provider's animation. A value or a ref (a function is a custom animation).

AutoAnimationvalue · ref · getter

As 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 · getter

The tree's own size follows its rows, as a list's does (animateSize). Default true.

boolean | AutoScrollOptionsvalue · ref · getter

Overrides the provider's autoScroll for this tree's scroll container. Read when the drag starts.

MaybeElementRef

As in SortableListOptions: the element that takes drops for the tree, default the tree's own.

booleanvalue · ref · getter

true: its rows can't be dragged, and it takes no items.

( event: SortableTreeDropEvent<T>, ) => MaybePromise<BeforeDropResult>

As in SortableListOptions.

(event: SortableTreeDropEvent<T>) => void

As in SortableListOptions: before the write, which event.preventDefault() skips.

Returns

What makeSortableTree returns.

Key

The 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.

() => void

Animates 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.

Key

The node's key (getKey).

T

Your node.

number

Its depth: roots are 0. The dragged node has its projected depth.

Key | null

Key of its parent; null for a root.

number

Index among siblings.

boolean

It has children, shown or not.

boolean

Its children are hidden (its key is in collapsed).

Projection

Where the dragged node would land in a tree.

number

The depth it would get (roots are 0).

Key | null

The node it would go under; null at the root.

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.

{ list: Key | null; index: number; parentKey: Key | null }

Where the node leading the drag was: its tree, its index among its siblings, its parent.

{ list: Key; index: number; parentKey: Key | null }

Where the first dropped node lands: its tree, its index among its new siblings, its new parent.