Building Sortable Tree — A Lightweight Drag & Drop Tree in Vanilla TypeScript

November 4, 2025  — 
 DevWeb

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.

                  Related Pages

                Open source often comes with an expectation: projects are best built together. And in many cases, that’s true. In my professional life, I collaborate constantly — pair programming, code reviews, team planning. I know firsthand the value of working with others, and I enjoy it.

                Cookie consent banners are, in my opinion, one of the worst things to have happened to the modern web. They clutter interfaces, confuse users, and ultimately serve as a band-aid for a problem that should be solved at the browser level — not by every single website reinventing the wheel in JavaScript.

                As an open-source maintainer, I deeply appreciate the importance of cybersecurity. Security is a shared responsibility — both for users who rely on software to be secure and for developers who build and maintain open-source projects. Responsible vulnerability reporting strengthens the ecosystem, helping us all build better, safer software. However, in recent years, the term "security researcher" has been stretched to the point where it is becoming counterproductive.

                Long-term open-source projects, such as Automad, require stability and independence from third-party libraries. Relying on external frameworks introduces risks that can impact maintainability, long-term support, and overall project longevity.

                As the developer of Automad, I wanted to make working with Automad themes in Neovim as smooth as possible. Automad has its own templating syntax, so I created tree-sitter-automad to provide proper syntax highlighting. Since it’s not yet an official parser, you need to manually register it in Neovim. To speed up template writing, you can also add custom snippets using LuaSnip. Here's how to set everything up.

                Creating a multilingual website can significantly expand your reach and user engagement by catering to a global audience. Automad 2, a lightweight CMS that prides itself on simplicity and flexibility, makes it easy to create and manage multilingual websites.

                Starting with the latest alpha 16 of Automad 2, Open-Graph images are rendered automatically for every page on the fly. Colors can be customized, a logo can be added. No further setup needed. 

                Automad 2.0 Alpha
                October 4, 2024
                 AutomadWeb

                After a long development process, the stable release of Automad 2 is getting closer. While many aspects of the system have evolved, the core vision remains the same — delivering a fast, flexible, and file-based content management system with a powerful templating engine.