Drag and drop with Svelte using svelte-dnd-action
Tags: webdev, svelte, javascript, showdev
Published: 2020-05-19 09:10:47

**TLDR**: this post will show you how you can add amazing drag and drop capabilities to your Svelte app using [svelte-dnd-action](https://github.com/isaacHagoel/svelte-dnd-action). If you've always wanted to build a Trello clone using Svelte (just with nicer animations than Trello's) you've come to the right place.

## Let's talk about drag and drop for a minute

If you've ever tried implementing an app that has rich (or even basic) drag and drop interactions, you would know that it is surprisingly difficult. Sure, the browser has [a built in drag and drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API). There is only a minor issue with it - it falls flat on its face when it comes to look and feel.
Don't believe me? [svelte-sortable-list](https://github.com/brunomolteni/svelte-sortable-list) is a library that uses the browser's drag and drop API and goes above and beyond (with the help of Svelte) to add as much animation as possible. Despite the admirable effort (I mean it), it is not something I could ship to production. The dragged element, as well as all the other elements, stay in their original place until a drop event takes place. It feels very static and stale ([you can try it for yourself](https://svelte.dev/repl/0b4fd50a87a94efd81b533229b43d941?version=3.6.4)). As Rich Harris says: "we can do better". 


React developers have been enjoying the mighty, although heavyweight and complicated, [react-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd). Svelte developers (at least yours truly) were left wanting.

[svelte-dnd-action](https://github.com/isaacHagoel/svelte-dnd-action) is a new library that aims to correct that.

## How does svelte-dnd-action work?
As its name suggests, the library uses [Svelte's actions mechanism](https://svelte.dev/tutorial/actions) in order to turn any list container to a drag and drop (dnd) zone. It relies on its host (=== your code) to update the list's data when requested to do so (via events). It also relies on its host to help out with some of the animations by using [Svelte's built-in flip animation](https://svelte.dev/tutorial/animate).
Let's look at a simple example;

Say we have the following component that displays a list with 3 items:
```html
<style>
	div {
		height: 1.5em;
		width: 10em;
		text-align: center;
		border: 1px solid black;
		margin: 0.2em;
		padding: 0.3em;
	}
</style>
<script>
	let items = [
		{id:1, title: 'I'},
		{id:2, title: 'Am'},
		{id:3, title: 'Yoda'}
	];
</script>
<section>
	{#each items as item(item.id)}
		<div>
			{item.title}	
		</div>
	{/each}
</section>
```

Now let's say we want to make it re-sortable using drag and drop. 
Let's add svelte-dnd-action into the mix:
```html
<style>
	div {
		height: 1.5em;
		width: 10em;
		text-align: center;
		border: 1px solid black;
		margin: 0.2em;
		padding: 0.3em;
	}
</style>
<script>
	import {dndzone} from 'svelte-dnd-action';
	function handleSort(e) {
		items = e.detail.items;
	}
	let items = [
		{id:1, title: 'I'},
		{id:2, title: 'Am'},
		{id:3, title: 'Yoda'}
	];
</script>
<section use:dndzone={{items}} on:consider={handleSort} on:finalize={handleSort}>
	{#each items as item(item.id)}
		<div>
			{item.title}	
		</div>
	{/each}
</section>
```
[Play with this example in the REPL](https://svelte.dev/repl/077cf720e2a6439caca5fb00d92d58a8?version=3.22.3)
Easy, right?
We pass out items into the `dndzone` action and update our list when we get a `consider` or `finalize` event. The difference between the two is that `consider` is emitted for intermediary states (as items need to 'make room') and `finalize` is emitted when the element is dropped. The distinction between the two can be useful when deciding whether to save the new list in the server for example.
**One important thing to notice** is that each item in the list has an `id` property which we also pass as the key for the `#each` block. `svelte-dnd-action` relies on the existence of the `id` property so make sure you have it.

This is neat and all, but there are no animations yet. In order to make everything animate nicely, we need to add `flip` into the mix and pass the flip duration into `dndzone` as a parameter:
```html
<style>
	div {
		height: 1.5em;
		width: 10em;
		text-align: center;
		border: 1px solid black;
		margin: 0.2em;
		padding: 0.3em;
	}
</style>
<script>
	import {dndzone} from 'svelte-dnd-action';
	import {flip} from 'svelte/animate';
	const flipDurationMs = 200;
	function handleSort(e) {
		items = e.detail.items;
	}
	let items = [
		{id:1, title: 'I'},
		{id:2, title: 'Am'},
		{id:3, title: 'Yoda'}
	];
</script>
<section use:dndzone={{items, flipDurationMs}} on:consider={handleSort} on:finalize={handleSort}>
	{#each items as item(item.id)}
		<div animate:flip={{duration:flipDurationMs}}>
			{item.title}	
		</div>
	{/each}
</section>
```
[Play with this example in the REPL](https://svelte.dev/repl/3b41be5109b34c38b7bf7be2ff7f50e1?version=3.22.3)
Viola, it animates!

## Dnd zones types
By default, if you use `dnd-zone` on multiple list containers, you will be able to grab an element from one list and drop it into another. That's pretty cool but sometimes you want control over what can go where. 
In order to address this need `svelte-dnd-action` accepts an optional `type` parameter.
[See it in action in the REPL](https://svelte.dev/repl/4d23eb3b9e184b90b58f0867010ad258?version=3.22.3).
In this example, you can move items between the two lists at the top, that have the type "light". You can't move items between the top lists and the bottom list, which has the type "dark" (luckily, Yoda and Luke are safe). You can still shuffle the item within each list like before. 

One useful way to use types is for nested dnd-zones. For example, if you are building a Trello like board, each column can be a `dndzone` (so items can be moved from one column to another) and the container that holds the columns can also be a `dndzone` of a different type. That way, the columns can be re-ordered independently of the items that they hold. 

## What else can it do?
There is actually quite a bit more that this library can do.
To see a more complex example that includes horizontal and vertical lists, a board (nested zones as explained above) and demos the auto-scroll feature, please have a look at [this REPL](https://svelte.dev/repl/e2ef044af75c4b16b424b8219fb31fd9?version=3.22.3).


That's all for today folks. Happy dragging and dropping.

**Edit Oct 3 2020**: The library is now fully accessible as well. You can read more [here](https://dev.to/isaachagoel/svelte-now-has-an-accessible-drag-and-drop-library-15p).