Container wraps a core SnapSort container and provides it to descendant Item and nested Container components through Svelte context.
<script lang="ts">
import { Container, Item } from "@snap-engine/snapsort-svelte";
const tasks = [
{ id: "design", label: "Design" },
{ id: "build", label: "Build" },
];
</script>
<Container
config={{ direction: "column", groupID: "tasks" }}
items={tasks}
getItemId={(task) => task.id}
>
{#snippet entry(task)}
<Item itemId={task.id}>{task.label}</Item>
{/snippet}
</Container> Each entry must render exactly one Item or nested Container whose itemId matches getItemId(entry).
Props
type ContainerProps<T> = {
config: ContainerConfig;
items?: T[];
getItemId?: (entry: T) => string;
entry: Snippet<[T]>;
ghost?: Snippet<[GhostInsertEvent]>;
before?: Snippet;
after?: Snippet;
itemId?: string;
container?: Container;
locked?: boolean;
selected?: boolean;
className?: string;
metadata?: Record<string, unknown>;
}; | Prop | Default | Description |
|---|---|---|
config | Required | SnapSort container configuration. |
items | [] | Data entries for this container. Omit for containers that only receive drops or hold static children. |
getItemId | (entry) => entry.id | Stable ID for each entry. |
entry | Required | Renders one Item or nested Container for each entry. |
ghost | Default spacer | Renders flow-mode target ghosts. Custom snippets should render <Ghost {event}>...</Ghost>. |
before | undefined | Non-sortable content rendered before entries. |
after | undefined | Non-sortable content rendered after entries. |
itemId | undefined | Required when this container is rendered as a sortable entry inside another container. |
container | Bindable | Receives the created core container object. |
locked | true | Keeps the container itself from being dragged when nested. Set to false for draggable nested containers. |
selected | false | Consumer-owned selection flag for multi-item drags. Only meaningful when locked is false. |
className | "" | Extra class string applied to the rendered element. |
metadata | {} | Metadata assigned to the container object and exposed in callback events. |
Container renders a div with snapsort-container plus a mode-specific class.
Config
| Option | Default | Description |
|---|---|---|
mode | "euclidean" | Drag/drop mode for this tree: "euclidean", "progressive", "insertion", or "swap". |
strategy | Resolved from mode | Advanced custom { dropTarget, lifecycle } strategy pair that overrides mode. |
direction | "column" | Main layout direction: "column" or "row". |
groupID | "default-group" | Containers with the same group can exchange items. |
mainAxisAlign | "start" | Main-axis alignment for virtual placement. Supports "start" and "center". |
name | Generated | Human-readable container name. |
animation | Default 100ms animations | Reorder, drop, and click-move animation settings. Set to null to disable configured animations. |
disableFlip | false | Disables FLIP movement animation. |
noDrop | false | Prevents the container from being a drop target. Useful for root layout containers. |
dropArea | false | Treats the container as an explicit drop area for collision filtering. |
callbacks | Default DOM callbacks | Hooks for DOM, framework state, and custom ghost behavior. |
Mode is resolved from the root container when a drag starts, so nested containers in one drag tree should use the same mode.
Callbacks
| Callback | When it runs |
|---|---|
onItemMove(event) | An item moves to a new container or index. Preferred for state-backed lists because it includes both from and to. |
onItemSwap(event) | Swap mode commits a pairwise slot exchange. Falls back to two onItemMove events when undefined. |
onItemInsert(event) | An item is inserted into a container; this is the lower-level fallback for onItemMove. |
onItemRemove(event) | An item is removed through removeItem(id). |
onDragStart(event) | A drag is starting. Return false to veto it. |
onDragEnd(event) | A drag has ended and its mutation has committed. |
onDropTargetChange(event) | The prospective drop container or index changed. Fires on the root container. |
canDrop(event) | Resolves whether this container accepts the current drag. Keep it cheap. |
onGhostInsert(event) | A ghost item or insertion marker is inserted or moved. |
onGhostRemove(event) | A ghost item or insertion marker is removed. |
createGhost(event) | Creates a marker-kind ghost for insertion or swap mode. |
flushMutation(mutation) | Runs a structural callback inside the adapter’s synchronous Svelte commit. Supplied automatically. |
awaitMutation() | Deprecated compatibility callback. |
For state-backed lists, update your Svelte arrays in onItemMove or onItemSwap. The adapter uses flushSync, allowing SnapSort to read final DOM geometry before paint.
Bindable Core Object
Use bind:container when buttons, keyboard handlers, or other controls need the core container API.
<script lang="ts">
import { Container, Item } from "@snap-engine/snapsort-svelte";
import type { Container as SnapSortContainer } from "@snap-engine/snapsort";
let source: SnapSortContainer | undefined = $state();
let target: SnapSortContainer | undefined = $state();
let sourceCards = $state([{ id: "card-1", label: "Card 1" }]);
let targetCards = $state<{ id: string; label: string }[]>([]);
function moveToTarget(id: string) {
if (!source || !target) return;
source.moveItem(id, target, target.numberOfItems);
}
</script>
<Container
bind:container={source}
config={{ direction: "column", groupID: "cards" }}
items={sourceCards}
getItemId={(card) => card.id}
>
{#snippet entry(card)}
<Item itemId={card.id}>
<button type="button" onclick={() => moveToTarget(card.id)}>Move card</button>
</Item>
{/snippet}
</Container>
<Container
bind:container={target}
config={{ direction: "column", groupID: "cards" }}
items={targetCards}
getItemId={(card) => card.id}
>
{#snippet entry(card)}
<Item itemId={card.id}>{card.label}</Item>
{/snippet}
</Container> | API | Description |
|---|---|
container.moveItem(id, targetContainer, index) | Moves an item into another container or index. |
container.removeItem(id) | Removes an item by itemId or internal object ID. |
container.numberOfItems | Number of tracked child items. |
container.groupID | Configured group ID. |
container.direction | Gets or sets "column" or "row". |
container.mainAxisAlign | Gets or sets "start" or "center". |
container.dropArea | Gets or sets explicit drop-area behavior. |
container.mode | Gets or sets the drag/drop mode. |
container.config | Live ContainerConfig object. |
container.callbacks | Configured callback object. |
container.dragSession | Active DragSession, or null. |
Custom Ghost
Use a ghost snippet with Ghost to customize the flow spacer in euclidean or progressive mode.
<Container config={{ mode: "progressive", direction: "row" }} items={tiles}>
{#snippet entry(tile)}
<Item itemId={tile.id}>{tile.text}</Item>
{/snippet}
{#snippet ghost(event)}
<Ghost {event}>
<span class="tile-ghost">{event.originalItemId}</span>
</Ghost>
{/snippet}
</Container>