Library / SDK
SortableJS/Sortable avatar
SortableJS/Sortable

SortableJS/Sortable: HTML5 drag-and-drop list reordering without a framework

Reorderable drag-and-drop lists for modern browsers and touch devices. No jQuery or framework required.

31,183 stars3,720 forksJavaScriptMIT

At a glance

What is it?
Sortable is a small MIT-licensed JavaScript library that turns any container of elements into a reorderable list using the native HTML5 drag and drop API. It is a good fit when you want DOM-level reordering and are willing to own the state sync yourself.
Who is it for?
Adopt Sortable if you have a plain DOM list, want cross-list dragging and touch support without pulling in a framework, and can wire the resulting order back into your own data model. Skip it if you need accessible keyboard reordering out of the box or if your list is rendered by a virtualized or framework-controlled tree, where Sortable will move nodes your renderer does not expect.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 175 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem Sortable solves, and the audience it assumes

Reordering a list in a browser is a deceptively large amount of work. The native HTML5 drag and drop API gives you dragstart, dragover and drop, but it does not give you a drop placeholder, cross-container transfers, autoscroll when the pointer nears the viewport edge, or a fallback for touch devices where the native API is unreliable. Sortable packages those behaviours behind a single constructor. The README describes it as "a JavaScript library for reorderable drag-and-drop lists", supporting touch devices and modern browsers including IE9, with dragging within one list or between lists.

The audience is narrow and specific. You are building a settings panel, a kanban column, a playlist editor, or a ranked list, and the markup is already in the DOM. You are not using a framework that owns the virtual DOM, or if you are, you are willing to accept that Sortable will mutate nodes underneath it. The README lists wrapper packages for React, Vue, Angular, Ember, Knockout, Polymer and Meteor, which tells you the maintainers expect framework users to go through those adapters rather than call Sortable directly against framework-rendered children.

How the mechanism actually works

Sortable attaches to a container element, not to the items inside it. You call Sortable.create(el) on the parent, and the library then decides which children are draggable based on the draggable option, which defaults to treating the container's children as the draggable set. This is why the README can say you may use "any element for the list and its elements, not just ul/li".

The README states the library is "built using native HTML5 drag and drop API". On top of that it layers its own visual state: ghostClass marks the drop placeholder, chosenClass marks the selected item, and dragClass marks the item being dragged. When a drag starts, Sortable inserts a placeholder element into the target position and moves it as the pointer crosses item boundaries. The swapThreshold and invertSwap options control where that boundary sits, and there is a wiki article titled "Swap Thresholds and Direction" for the details.

Cross-list movement is governed by the group option, which can be a plain string or an object with pull and put keys taking true, false, 'clone' or an array. That is the whole access-control model: a list declares what it will give up and what it will accept. Events fire at each stage (onChoose, onUnchoose, onStart, onEnd, and others), and the event object carries evt.item and evt.oldIndex, so your code reads the final position after the fact rather than driving the drag.

Installing it and the options you will actually touch

The README gives three install paths. With npm: npm install sortablejs --save. With Bower: bower install --save sortablejs. There is also a CDN badge in the header and a linked demo at sortablejs.github.io/Sortable.

Imports come in three flavours. import Sortable from 'sortablejs' is the default build. import Sortable from 'sortablejs/modular/sortable.core.esm.js' gives you the core without default plugins. import Sortable from 'sortablejs/modular/sortable.complete.esm.js' includes all plugins. Plugins are mounted explicitly, and the README's example is Sortable.mount(new MultiDrag(), new Swap()). TypeScript definitions live in a separate package, @types/sortablejs.

The options block is where the real decisions are. animation takes milliseconds and controls the move animation, with 0 disabling it. handle takes a selector such as .my-handle so only part of an item initiates the drag, while filter takes selectors that should never start a drag. delay is documented as the time in milliseconds before sorting begins, and delayOnTouchOnly restricts that delay to touch input. touchStartThreshold is a pixel distance the pointer must move before a delayed drag is cancelled. Those three interact, and getting them wrong is the usual cause of a list that feels broken on a phone. forceFallback and the fallbackClass, fallbackOnBody and fallbackTolerance options let you abandon native HTML5 behaviour entirely and use the library's own clone-based approach. toArray() reads order back out using the attribute named by dataIdAttr, which defaults to data-id.

Where Sortable stops being the right tool

Sortable moves real DOM nodes. That is the design, and it is also the failure mode. If a framework owns the children of your container and re-renders from its own state, Sortable's placeholder insertion and node movement happen outside that state. The result is a list that looks correct until the next render, at which point the framework puts things back. The existence of separate React, Vue and Angular wrapper packages is the maintainers' answer to this, and it is a reasonable one, but it means the plain library is not the thing you want in a framework-rendered tree.

Accessibility is the second gap. The README's feature list covers touch devices, CSS animation, handles, auto-scrolling, swap detection and multi-drag. It does not claim keyboard-driven reordering. Native HTML5 drag and drop is not keyboard-operable in the general case, and nothing in the README suggests Sortable adds a keyboard path. If your list must be reorderable by keyboard, that work is yours.

The third constraint is the browser baseline. The README says modern browsers "including IE9". That is a historical statement, not a current support promise, and it implies the library carries compatibility code you may not need. forceFallback exists precisely because native drag and drop is inconsistent across environments, and reaching for it means you are now maintaining a fallback clone element in the DOM, with fallbackOnBody and removeCloneOnHide controlling where it lives and when it is cleaned up.

What you would use instead, and how the approach differs

The most direct alternative is the native HTML5 drag and drop API on its own. You set draggable="true" on items, listen for dragstart and dragover, call preventDefault on the latter, and compute the insertion point from pointer coordinates in a drop handler. The difference is not capability but ownership. Native gives you no placeholder element, no cross-list group rules, no autoscroll, and no touch fallback. Sortable's group option, ghostClass and auto-scrolling are exactly the parts you would otherwise write yourself, and the README positions the library against a named predecessor, claiming drag handles with selectable text are "better than voidberg's html5sortable".

A second alternative is a framework-specific sortable component, for instance the React or Vue wrappers the README links to. Those wrappers do not change the underlying mechanism; they change who is allowed to mutate the list. If your data already lives in component state, a wrapper that emits a new ordering and lets the framework re-render avoids the desync described above. The trade-off is that you inherit the wrapper's release cadence rather than Sortable's. Note that the README lists wrappers for React 2.0+ and React Mixin, Angular 1 and 2.0+, Vue, Ember, Knockout, Polymer and Meteor, so the wrapper ecosystem is broad but each entry is a separate project with its own maintenance.

Maintenance cost, licence and what the release history shows

Sortable is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is the standard permissive arrangement and it is the reason a library like this can be embedded in a closed-source product. This is a description of the licence text, not legal advice; if your organisation has a policy on third-party licences, run it through that process.

The release history in the repository shows 1.15.5 and 1.15.6 landing one day apart in late November 2024, then 1.15.7 in February 2026. The repository's last push is dated 2026-03-24, and it is not archived. Reading those dates together: the project is not abandoned, but it is not on a rapid release cadence either. A roughly fourteen-month gap between 1.15.6 and 1.15.7 means you should not expect frequent breaking changes, and equally should not expect quick turnaround on a bug you file.

The upgrade surface is small. The public API is a constructor plus an options object plus events, and the plugin system is opt-in through Sortable.mount. That means most upgrades are a version bump in package.json and a check that your mounted plugins still exist. The real maintenance cost sits elsewhere: the CSS classes you define for ghostClass, chosenClass and dragClass, and the code that reads toArray() and writes the new order back to your server. Those are yours regardless of what version of Sortable you run. If you use the modular builds, note that switching between sortablejs, sortable.core.esm.js and sortable.complete.esm.js changes which plugins are present, so a version bump that also changes your import path is a behaviour change, not just a dependency refresh.

Editorial conclusion

Adopt Sortable if you have a plain DOM list, want cross-list dragging and touch support without pulling in a framework, and can wire the resulting order back into your own data model. Skip it if you need accessible keyboard reordering out of the box or if your list is rendered by a virtualized or framework-controlled tree, where Sortable will move nodes your renderer does not expect. Before committing, check the options block in the README for delay, delayOnTouchOnly and touchStartThreshold, since those govern how the library behaves on touch devices, and confirm the version you install from npm resolves to 1.15.7 or later.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. SortableJS/Sortable on GitHub
Community notes

Community notes