Default
To add a basic reorderable list, use the
data-kt-sortable
attribute on the list container (e.g. a
<ul>
or
<div>
). Items will be draggable and reorderable when the page loads.
- Newsletter
- Downloads
- Team Account
<ul class="flex flex-col w-full max-w-xs" data-kt-sortable="true" data-kt-sortable-animation="150" id="kt-sortable-default">
<li class="inline-flex items-center gap-x-3 py-3 px-4 cursor-grab text-sm font-medium bg-white border border-layer-line text-layer-foreground -mt-px first:rounded-t-lg first:mt-0 last:rounded-b-lg">
Newsletter
</li>
<li class="inline-flex items-center gap-x-3 py-3 px-4 cursor-grab text-sm font-medium bg-white border border-layer-line text-layer-foreground -mt-px first:rounded-t-lg first:mt-0 last:rounded-b-lg">
Downloads
</li>
<li class="inline-flex items-center gap-x-3 py-3 px-4 cursor-grab text-sm font-medium bg-white border border-layer-line text-layer-foreground -mt-px first:rounded-t-lg first:mt-0 last:rounded-b-lg">
Team Account
</li>
</ul>
Handle
Restrict dragging to a handle element by setting
data-kt-sortable-handle
to a CSS selector (e.g.
.kt-sortable-handle
). Only dragging from the handle will reorder items.
- Newsletter
- Downloads
- Team Account
<ul class="flex flex-col w-full max-w-xs" data-kt-sortable="true" data-kt-sortable-animation="150" data-kt-sortable-handle=".kt-sortable-handle" id="kt-sortable-handle">
<li class="inline-flex items-center gap-x-3 py-3 px-4 text-sm font-medium bg-white border border-layer-line text-layer-foreground -mt-px first:rounded-t-lg first:mt-0 last:rounded-b-lg">
<span class="kt-sortable-handle cursor-grab shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Newsletter
</li>
<li class="inline-flex items-center gap-x-3 py-3 px-4 text-sm font-medium bg-white border border-layer-line text-layer-foreground -mt-px first:rounded-t-lg first:mt-0 last:rounded-b-lg">
<span class="kt-sortable-handle cursor-grab shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Downloads
</li>
<li class="inline-flex items-center gap-x-3 py-3 px-4 text-sm font-medium bg-white border border-layer-line text-layer-foreground -mt-px first:rounded-t-lg first:mt-0 last:rounded-b-lg">
<span class="kt-sortable-handle cursor-grab shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Team Account
</li>
</ul>
Shared Lists
Use the same
data-kt-sortable-group
value on two or more list containers to allow moving items between lists. Items can be dragged from one list and dropped into another.
Nested
Nested sortables use a separate sortable list container for each level. Give every container the same
group
(e.g.
nested
) so items can be dragged between parent and child lists. For nested lists,
fallbackOnBody: true
and
swapThreshold: 0.65
are recommended for smoother drag behavior.
Item 1.1
Item 2.1
Item 2.2
Item 3.1
Item 3.2
Item 3.3
Item 2.3
Item 2.4
Item 1.2
Item 2.5
Item 2.6
Item 1.3
Item 1.4
Item 1.5
<div class="space-y-1" id="kt-sortable-nested-root">
<div class="nested-sortable space-y-1" data-kt-sortable="true" data-kt-sortable-animation="150" data-kt-sortable-group="nested">
<div class="nested-item space-y-1">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-sky-50 dark:bg-sky-950/40 border border-sky-200 dark:border-sky-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 1.1
</div>
<div class="ps-5 space-y-1 nested-sortable" data-kt-sortable="true" data-kt-sortable-animation="150" data-kt-sortable-group="nested">
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-violet-50 dark:bg-violet-950/40 border border-violet-200 dark:border-violet-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 2.1
</div>
</div>
<div class="nested-item space-y-1">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-violet-50 dark:bg-violet-950/40 border border-violet-200 dark:border-violet-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 2.2
</div>
<div class="ps-5 space-y-1 nested-sortable" data-kt-sortable="true" data-kt-sortable-animation="150" data-kt-sortable-group="nested">
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-amber-50 dark:bg-amber-950/40 border border-amber-200 dark:border-amber-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 3.1
</div>
</div>
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-amber-50 dark:bg-amber-950/40 border border-amber-200 dark:border-amber-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 3.2
</div>
</div>
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-amber-50 dark:bg-amber-950/40 border border-amber-200 dark:border-amber-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 3.3
</div>
</div>
</div>
</div>
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-violet-50 dark:bg-violet-950/40 border border-violet-200 dark:border-violet-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 2.3
</div>
</div>
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-violet-50 dark:bg-violet-950/40 border border-violet-200 dark:border-violet-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 2.4
</div>
</div>
</div>
</div>
<div class="nested-item space-y-1">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-sky-50 dark:bg-sky-950/40 border border-sky-200 dark:border-sky-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 1.2
</div>
<div class="ps-5 space-y-1 nested-sortable" data-kt-sortable="true" data-kt-sortable-animation="150" data-kt-sortable-group="nested">
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-violet-50 dark:bg-violet-950/40 border border-violet-200 dark:border-violet-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 2.5
</div>
</div>
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-violet-50 dark:bg-violet-950/40 border border-violet-200 dark:border-violet-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 2.6
</div>
</div>
</div>
</div>
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-sky-50 dark:bg-sky-950/40 border border-sky-200 dark:border-sky-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 1.3
</div>
</div>
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-sky-50 dark:bg-sky-950/40 border border-sky-200 dark:border-sky-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 1.4
</div>
</div>
<div class="nested-item">
<div class="py-2 px-3 flex items-center gap-x-2 cursor-grab bg-sky-50 dark:bg-sky-950/40 border border-sky-200 dark:border-sky-800 rounded-lg font-medium text-sm text-layer-foreground">
<span class="shrink-0 size-4 text-muted-foreground">
<i class="ki-outline ki-menu">
</i>
</span>
Item 1.5
</div>
</div>
</div>
</div>
const containers = document.querySelectorAll('#kt-sortable-nested-root .nested-sortable');
containers.forEach((el) => {
new Sortable(el, {
group: 'nested',
animation: 150,
fallbackOnBody: true,
swapThreshold: 0.65
});
});
Methods
The following methods are available on the KTSortable component.
| Method | Description |
|---|---|
KTSortable.init()
|
Initializes all elements on the page that have the
data-kt-sortable
attribute (and are not disabled with
data-kt-sortable="false"
). Called automatically when the page loads.
|
KTSortable.getInstance(element)
|
Returns the KTSortable instance for the given DOM
element
(or selector). Returns
null
if no instance exists.
|
new KTSortable(element, options)
|
Creates a KTSortable instance for the given DOM
element
with optional
options
object. Use for programmatic initialization.
|
dispose()
|
Destroys the Sortable.js instance, removes event listeners, and cleans up the DOM element reference. Call on an instance obtained via
getInstance()
.
|
getSortable()
|
Returns the underlying Sortable.js instance for advanced usage and direct API access. |
getElement()
|
Returns the DOM element associated with this KTSortable instance. |
Options
Configuration options can be set via data attributes (e.g.
data-kt-sortable-animation
) or passed to the constructor. Options are passed through to Sortable.js where applicable.
| Option | Type | Default | Description |
|---|---|---|---|
animation
|
number
|
150
|
Animation duration in milliseconds when sorting. |
handle
|
string
|
—
|
CSS selector for the drag handle. If set, only the handle element starts a drag. |
group
|
string | object
|
—
|
Group name (string) or options object to allow dragging between multiple lists. Use the same value on each list to share items. |
dragClass
|
string
|
'rounded-none!'
|
CSS class applied to the dragged element. |
ghostClass
|
string
|
—
|
CSS class applied to the placeholder (ghost) element. |
chosenClass
|
string
|
—
|
CSS class applied to the chosen (selected) item. |
lazy
|
boolean
|
false
|
If true, the sortable is not created in the constructor; you must call init or create the instance later. |
fallbackOnBody
|
boolean
|
false
|
Append the drag clone to document.body. Recommended for nested sortables to avoid scroll/overflow issues. |
swapThreshold
|
number
|
1
|
Percentage of the target that must be overlapped to trigger a swap (0–1). Lower values (e.g. 0.65) make reordering in nested lists more responsive. |
Events
KTSortable dispatches the following DOM events on the sortable element. Listen with
element.addEventListener('kt.sortable.update', handler)
.
| Event | Description |
|---|---|
kt.sortable.init
|
Fired when a KTSortable instance is created. Detail:
{ element }
.
|
kt.sortable.start
|
Fired when dragging starts. Detail:
{ item, from, oldIndex }
.
|
kt.sortable.end
|
Fired when dragging ends. Detail:
{ item, from, to, oldIndex, newIndex }
.
|
kt.sortable.update
|
Fired when the list order has changed (item dropped in new position). Detail:
{ item, from, to, oldIndex, newIndex }
.
|
Data Attribute Initialization
Place these attributes on the list container element (e.g.
<ul>
or
<div>
). Attribute names use the
data-kt-sortable-*
prefix; the part after the prefix is the option name in camelCase (e.g.
data-kt-sortable-animation
→
animation
).
| Data Attribute | Type | Default | Description |
|---|---|---|---|
data-kt-sortable
|
boolean
|
true
|
Enables auto-initialization of the sortable on this element. Set to
false
to exclude from init.
|
data-kt-sortable-animation
|
number
|
150
|
Animation duration in milliseconds when sorting. |
data-kt-sortable-handle
|
string
|
—
|
CSS selector for the drag handle. Only this element starts a drag. |
data-kt-sortable-group
|
string
|
—
|
Group name. Use the same value on multiple lists to allow moving items between them. |
data-kt-sortable-drag-class
|
string
|
'rounded-none!'
|
CSS class applied to the element while dragging. |
data-kt-sortable-ghost-class
|
string
|
—
|
CSS class applied to the placeholder (ghost) element. |
data-kt-sortable-chosen-class
|
string
|
—
|
CSS class applied to the selected item. |
data-kt-sortable-lazy
|
boolean
|
false
|
If true, the sortable is not created automatically; initialize programmatically with
new KTSortable(element)
.
|
JavaScript Initialization
By default,
KTSortable.init()
is called on page load and initializes every element with
data-kt-sortable
. To create an instance manually or pass options, use the constructor.
Programmatic initialization with options:
const element = document.querySelector("#my-list");
const sortable = new KTSortable(element, {
animation: 200,
handle: ".drag-handle",
group: "shared"
});
// Later: get instance or dispose
const instance = KTSortable.getInstance(element);
instance.dispose();
Re-initialize after dynamic content:
Call
KTSortable.createInstances()
or
KTSortable.init()
to pick up new elements with
data-kt-sortable
. Ensure previous instances are disposed if the same nodes are re-used.