# sveltednd gives Svelte 5 two actions instead of a drag and drop framework

> sveltednd is a TypeScript drag and drop library for Svelte 5 built on runes, with HTML5 drag on desktop, pointer events on touch, nested containers and opt-in keyboard reordering. It ships with zero external dependencies, and the business of moving items stays in your own code.

**thisuxhq/sveltednd** — A lightweight, flexible drag and drop library for Svelte 5 applications.

- Repository: https://github.com/thisuxhq/sveltednd
- Website: https://sveltednd.thisux.com
- Stars: 613 · Forks: 24
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/thisuxhq-sveltednd

## Two actions, and the state change stays yours

sveltednd is a lightweight, flexible drag and drop library for Svelte 5 applications, built with TypeScript and the runes system. The shape of the API is the design decision that matters. There is no store to subscribe to and no reducer to configure. Instead there are two Svelte actions, use:draggable and use:droppable, plus a global dndState object for reading what is happening. A draggable element is told which container it belongs to and what data it carries, a droppable element is told which container it is and what to call when something is dropped on it, and the reorder logic itself is ordinary code in your component. In the quick start example, the drop handler receives a DragDropState and works out the indices by hand, splicing the array. That is more work for the developer than a declarative library would be, and it is also the point: the library handles the pointer mechanics and the indicator rendering, and leaves the meaning of a move to the application. It reports a lightweight package with zero external dependencies and a minimal footprint.

## HTML5 drag on desktop, pointer events on touch

The dual mode design answers a real problem with drag and drop on the web. The native HTML5 drag and drop API is well supported on desktop and gives you a drag image and OS integration for free, but it does not work on touch screens, where you get page scrolling instead of a drag. sveltednd uses the HTML5 Drag API for desktop and Pointer Events for touch and mobile, and the developer does not have to choose. The cost of that convenience is a slightly larger code path than a single implementation, and it means the behaviour you get is not identical on both sides. Pointer events also give finer control over the drag threshold and the pointer capture, which is why libraries that skip the HTML5 path often feel more responsive on trackpads and touch screens. The feature list also includes a smart interaction behaviour that automatically protects interactive elements such as inputs and buttons, so dragging a row that contains a checkbox does not start a drag every time someone clicks the checkbox. You can extend that protection with the interactive option, which takes a list of additional selectors.

## Drop indicators and drag handles

Two options decide how the drag feels in a dense interface. A drop indicator gives visual feedback showing exactly where the item will land, and the droppable options expose a direction property with the values vertical, horizontal and grid, defaulting to vertical, which is how the library knows where to draw the indicator line in a multi column layout. A drag handle is the opposite idea: an optional CSS selector that restricts dragging to a specific element inside the draggable, so the rest of a card stays clickable. The attribute options exist for the same reason, letting you override the CSS class that is applied while dragging or while a drop target is hovered, which is what lets the indicator blend into a design system rather than fighting it.

## Nested containers need containerGroup

Nested support is the case where most drag and drop libraries get vague, and sveltednd names its solution. It works with nested containers and complex hierarchies, and the containerGroup option is what keeps the drop indicators on matching levels. The idea is that a group identifier is attached to containers that belong to the same level of the hierarchy, so a container in one group will not show an indicator that belongs to another group, even when one is nested inside the other. Without that, dragging a card into a column inside a board would produce indicators from every ancestor level at once, and the user cannot tell which one the drop will actually target. The DragDropState interface makes the current target explicit, with sourceContainer, targetContainer, targetElement and a dropPosition of before or after, and an optional invalidDrop flag for the case where the pointer is over a zone that will not accept the item.

## Keyboard reordering and attachments

Two newer capabilities round out the feature set. Keyboard support is opt-in, with Space to pick up an item, arrow keys to move it and Escape to cancel, and it comes with screen reader announcements so the change is spoken rather than only drawn. That is the difference between a drag and drop implementation that is merely usable with a keyboard and one that is usable with a screen reader. The second is attachments, first class factories written for the newer Svelte attachment API, which the documentation marks as requiring Svelte 5.29 or newer. The action form and the attachment form solve the same problem, so a project on a recent Svelte can take the newer route and older projects stay on use:draggable. The project also ships an agent skill, a set of API patterns, reorder recipes, attachment examples and keyboard guidance, installable by coding agents.

## Installing and the first drag

The package is published under the scope @thisux/sveltednd, and the install command works with every major package manager.

```bash
npm install @thisux/sveltednd
# or
bun add @thisux/sveltednd
# or
yarn add @thisux/sveltednd
# or
pnpm add @thisux/sveltednd
```

The quick start then does two things: it holds the list in a rune, and it writes the reorder logic in the component.

```svelte
<script lang="ts">
	import { draggable, droppable, type DragDropState } from '@thisux/sveltednd';

	let items = $state(['Item 1', 'Item 2', 'Item 3']);

	function handleDrop(state: DragDropState<string>) {
		const { draggedItem, sourceContainer, targetContainer, dropPosition } = state;
```

The handler receives the state and works out the new position itself, which is the part that looks like boilerplate until you realise it is the only place your ordering rules live.

```svelte
		// Simple reordering logic
		const dragIndex = items.indexOf(draggedItem);
		let dropIndex = parseInt(targetContainer ?? '0');
		if (dropPosition === 'after') dropIndex++;

		if (dragIndex !== -1) {
			const [item] = items.splice(dragIndex, 1);
			const adjusted = dragIndex < dropIndex ? dropIndex - 1 : dropIndex;
			items.splice(adjusted, 0, item);
		}
```

The markup applies both actions to each row and passes the index as the container id, which is a trick worth understanding, because a container id that changes with the item position is what makes the target container name usable as a drop index.

```svelte
{#each items as item, index (item)}
	<div
		use:draggable={{ container: index.toString(), dragData: item }}
		use:droppable={{ container: index.toString(), callbacks: { onDrop: handleDrop } }}
	>
		{item}
	</div>
{/each}
```

There is also an agent skill with API patterns, reorder recipes, attachment examples and keyboard guidance, which coding agents can install on their own.

```bash
npx skills add thisuxhq/sveltednd
# or a single skill
npx skills add thisuxhq/sveltednd --skill sveltednd
```

## The TypeScript contract and the release rhythm

The options are typed rather than documented only in prose, and the interfaces are the quickest way to understand what the library hands you. The draggable options require a container identifier and a dragData payload, and add disabled, handle, interactive, callbacks and attributes on top. The droppable options require a container identifier and add disabled, direction, callbacks and attributes. The callback interface covers onDragStart, onDragEnd, onDragEnter, onDragLeave, onDragOver and onDrop, and the onDrop callback is allowed to be asynchronous, which is what makes it possible to persist an order to a server and then update the local list. The state object itself is a small generic interface.

```typescript
interface DragDropState<T = unknown> {
	isDragging: boolean; // Currently dragging?
	draggedItem: T; // Item being dragged
	sourceContainer: string; // Origin container ID
	targetContainer: string | null; // Current drop target
	targetElement: HTMLElement | null; // Element under cursor
	dropPosition: 'before' | 'after' | null; // Where item will drop
	invalidDrop?: boolean; // Over invalid drop zone?
}
```

Since dragData is generic, the payload type follows whatever you put in it. The repository is a full Svelte project rather than just a library folder, with a src directory, skills, static assets, svelte.config.js, vite.config.ts, a Tailwind config, a PostCSS config, an ESLint config, Prettier settings, husky hooks and commitlint, a release-please configuration, a wrangler.toml for Cloudflare, and both a .node-version and an .nvmrc. The licence is MIT, the copyright line names THISUX Private Limited, and the package version is 0.8.0. The release history shows v0.6.1 and v0.7.0 both on 2026-07-19 and v0.8.0 on 2026-09-01, with the last push on 2026-09-01.

## Conclusion

sveltednd suits a Svelte 5 project that needs sorting lists, kanban boards or nested containers and wants to own the state change in plain TypeScript rather than through a store. It does not suit a project still on Svelte 4, since the library is built on the runes system, and the attachment factory needs Svelte 5.29 or newer. Before you adopt it, install the package with npm install @thisux/sveltednd, check that the drop indicator behaviour fits your layout, and decide whether you want the drag handled by the HTML5 API on desktop or by pointer events everywhere, since the library supports both.

## FAQ

### How do I install sveltednd?

The package is @thisux/sveltednd and the README lists npm install @thisux/sveltednd, with bun add, yarn add and pnpm add shown as alternatives. The library has zero external dependencies.

### How does sveltednd work with touch screens?

It uses a dual mode design, the HTML5 Drag API for desktop and Pointer Events for touch and mobile support, so the same component works on both without the developer choosing an implementation.

### How do I reorder a list with sveltednd?

You attach use:draggable and use:droppable to the elements and implement the reorder yourself in the onDrop callback, working from the DragDropState, which gives you the dragged item, the source and target container and a drop position of before or after. The quick start example does exactly that with array splicing.

### Does sveltednd support nested containers and keyboard reordering?

Yes to both. Nested hierarchies are handled with the containerGroup option, which keeps drop indicators on matching levels, and keyboard reordering is opt-in, using Space to pick up, arrow keys to move and Escape to cancel, with screen reader announcements.

## Sources

- [License: MIT](https://github.com/thisuxhq/sveltednd/blob/main/LICENSE)
- [Project website](https://sveltednd.thisux.com)
- [README](https://github.com/thisuxhq/sveltednd/blob/main/README.md)
- [Releases](https://github.com/thisuxhq/sveltednd/releases)
- [thisuxhq/sveltednd on GitHub](https://github.com/thisuxhq/sveltednd)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/thisuxhq-sveltednd
