Building Sortable Tree — A Lightweight Drag & Drop Tree in Vanilla TypeScript
When working on the Automad dashboard, I needed a collapsible and sortable navigation tree for the page browser sidebar — something that would feel like a small file explorer inside the browser. My goal was simple: use plain TypeScript, no frameworks, and rely only on native browser APIs.
So I went looking for an existing library. There are countless tree components on npm — but nearly all of them depend on React, Vue, or another frontend framework. I wanted something that works in any context: a small script I could drop into a project and wire up using plain events and DOM nodes.
Nothing like that existed.
So I built it.
From a Small Utility to a Full Library
What started as a small experiment for the Automad dashboard eventually became a complete standalone library: sortable-tree — a lightweight, dependency-free (well, almost) TypeScript package that can create foldable and sortable tree structures with drag-and-drop support.
It’s designed for file explorers, navigation editors, or any interface where a nested structure needs to be reorganized interactively. No virtual DOMs. No framework abstractions. Just events, DOM nodes, and TypeScript.
Features
For the Automad dashboard, I needed a tree that could do a few very specific things: foldable branches, sortable nodes via drag-and-drop, and a way to keep the state even if the page reloads. It also had to be fully stylable so it could match the dashboard’s clean look — and all of this had to work without pulling in a framework. sortable-tree was built exactly to meet these needs.
- Written in plain TypeScript, framework-agnostic
- Drag and drop sorting
- Foldable branches
- Optional confirmation when moving nodes around
- Persisting state across page reloads
- Fully stylable — customize icons, node templates, and CSS
- (Almost) no dependencies
Basic Example
Here’s a minimal setup that renders a simple tree that persits state across page reloads:
import SortableTree, { SortableTreeNodeData } from "sortable-tree";
import "sortable-tree/dist/sortable-tree.css"; // basic styles
const nodes: SortableTreeNodeData[] = [
{
data: { title: "Home" },
nodes: [
{ data: { title: "Page 1" }, nodes: [] },
{
data: { title: "Page 2" },
nodes: [{ data: { title: "Subpage" }, nodes: [] }],
},
],
},
];
const tree = new SortableTree({
nodes: nodes,
element: document.querySelector("#tree"),
stateId: "my-tree",
initCollapseLevel: 2,
renderLabel: (data) => {
return `<span>${data.title}</span>`;
},
confirm: async (moved, parentNode) => {
return true;
},
onChange: ({
nodes,
movedNode,
srcParentNode,
targetParentNode
}) => {
console.log(movedNode.data);
},
onClick: (event, node) => {
console.log(node.data);
},
});
The library takes care of all drag-and-drop logic, nesting, and fold state management internally. It provides simple callback options like onClick and onChange to handle user interactions directly in your code. You can, for example, listen to when a node is moved or clicked and react accordingly — or even define optional confirmation logic before changes are applied. If you’d like to see it in action, check out the live demo and examples — you can play around with drag-and-drop, folding, and custom styling right in your browser.
Integrating in Real Projects
In Automad, this library powers the page browser sidebar. Pages can be folded and reordered with drag-and-drop, just like files in a file explorer.
Because Automad’s dashboard is written entirely in plain TypeScript — no frameworks — it’s a perfect example of how simple browser APIs can scale. The code is easy to read, inspect, and extend.
This simplicity is intentional. I wrote more about why Automad doesn’t rely on frameworks in another blog post — but in short: the less you depend on abstractions, the longer your software will live.
Try It Yourself
You can install the package directly from npm:
npm install sortable-tree
Then import it in your TypeScript or JavaScript project and render your first tree. Documentation, demos, and additional examples are available on the docs and demo site. The full source code is hosted on GitHub.
Closing Thoughts
What started as a missing piece in the Automad dashboard turned into a reusable, framework-free library for others to build on. From my perspective, sortable-tree shows that even complex UI patterns like nested drag-and-drop don’t need heavy dependencies — just a clear understanding of how the browser already works and what your project really needs.
It’s fully customizable, keeps state across reloads, and works anywhere TypeScript or JavaScript runs — a small tool that perfectly fits the kind of straightforward, practical approach Automad embraces.