Quickstart
On this page
Install
SnapSort is published on npm and supports npm, pnpm, Yarn, and Bun. This
quickstart uses Asset Base’s Engine, so install both packages with your
package manager:
npm install @snap-engine/snapsort @snap-engine/asset-basepnpm add @snap-engine/snapsort @snap-engine/asset-baseyarn add @snap-engine/snapsort @snap-engine/asset-basebun add @snap-engine/snapsort @snap-engine/asset-baseYour First Sortable List
Wrap SnapSort components in an Engine, configure the collection callback,
and render one Item per entry. This creates a working drag-and-drop list:
<script lang="ts">
// Import SnapEngine which SnapSort is built on top of.
import { Engine } from "@snap-engine/asset-base/svelte";
// Import the components.
import { Container, Ghost, Item } from "@snap-engine/snapsort/svelte";
// Import types and helpers to manage the items being dragged.
import {
createRenderEntries,
createRenderTree,
reduceRenderTree,
type ContainerCallbacks,
type GhostLifecycleEvent,
type ItemMoveEvent,
} from "@snap-engine/snapsort";
// Define a type for the JSON data representing each item.
type Task = { id: string; label: string };
// Build the data structure representing the initial list.
// Use $state.raw to prevent the framework from re-rendering
// the UI repeatedly as the helper functions mutate nested structures.
let list = $state.raw(
createRenderTree(
createRenderEntries<Task>(
[
{ id: "design", label: "Design" },
{ id: "build", label: "Build" },
{ id: "ship", label: "Ship" },
],
(task) => task.id,
),
),
);
// Hook up the helper to update the list state.
// Advanced users may define their own callbacks.
function onItemMove(event: ItemMoveEvent) {
list = reduceRenderTree(list, event);
}
function onGhostMove(event: GhostLifecycleEvent) {
list = reduceRenderTree(list, event);
}
const callbacks = {
onItemMove,
onGhostInsert: onGhostMove,
onGhostMove,
onGhostRemove: onGhostMove,
} satisfies ContainerCallbacks;
</script>
<Engine id="quick-start">
<Container itemId="quick-start-root" config={{ callbacks }}>
{#each list.entries as entry (entry.itemId)}
{#if entry.isGhost}
<Ghost ghost={entry.ghost} />
{:else}
<Item itemId={entry.itemId}>{entry.value.label}</Item>
{/if}
{/each}
</Container>
</Engine>import { useCallback, useState } from "react";
// Import the components.
import { Container, Engine, Ghost, Item } from "@snap-engine/snapsort/react";
// Import types and helpers to manage the items being dragged.
import {
createRenderEntries,
createRenderTree,
reduceRenderTree,
} from "@snap-engine/snapsort";
import type { GhostLifecycleEvent, ItemMoveEvent } from "@snap-engine/snapsort";
// Define a type for the JSON data representing each item.
type Task = { id: string; label: string };
// Build the data structure representing the initial list.
function createInitialList() {
return createRenderTree(
createRenderEntries<Task>(
[
{ id: "design", label: "Design" },
{ id: "build", label: "Build" },
{ id: "ship", label: "Ship" },
],
(task) => task.id,
),
);
}
export function QuickStart() {
const [list, setList] = useState(createInitialList);
// Hook up the helper to update the list state.
// Advanced users may define their own callbacks.
const onItemMove = useCallback((event: ItemMoveEvent) => {
setList((current) => reduceRenderTree(current, event));
}, []);
const onGhostMove = useCallback((event: GhostLifecycleEvent) => {
setList((current) => reduceRenderTree(current, event));
}, []);
return (
<Engine id="quick-start">
<Container
itemId="quick-start-root"
config={{
callbacks: {
onItemMove,
onGhostInsert: onGhostMove,
onGhostMove,
onGhostRemove: onGhostMove,
},
}}
>
{list.entries.map((entry) =>
entry.isGhost ? (
<Ghost key={entry.itemId} ghost={entry.ghost} />
) : (
<Item key={entry.itemId} itemId={entry.itemId}>
{entry.value.label}
</Item>
),
)}
</Container>
</Engine>
);
}// Initialize the engine and container.
import { Engine } from "@snap-engine/core";
import { CollisionEngine } from "@snap-engine/core/collision";
import { Container, Item } from "@snap-engine/snapsort";
const root = document.querySelector("#quick-start");
const list = root.querySelector("[data-snapsort-container]");
const engine = new Engine();
engine.setCollisionEngine(new CollisionEngine());
engine.assignDom(root);
const container = new Container(engine, null, { itemId: "quick-start-root" });
container.element = list;
// Assume metadata for each item is defined in the data attributes.
for (const element of list.querySelectorAll("[data-item-id]")) {
const itemId = element.dataset.itemId;
const item = new Item(engine, container, { itemId });
item.element = element;
}