@mikolajbadyl/svelte-gridstack
v0.1.1
Published
Svelte 5 wrapper for gridstack.js - reactive dashboard layout with drag-and-drop
Downloads
309
Maintainers
Readme
svelte-gridstack
Svelte 5 wrapper for gridstack.js - a TypeScript-first, reactive dashboard layout library with drag-and-drop and resize support.
Installation
npm install svelte-gridstack gridstack
gridstackis a peer dependency - you control the version.
Quick Start
<script lang="ts">
import 'gridstack/dist/gridstack.min.css';
import { GridStack, GridStackItem } from 'svelte-gridstack';
</script>
<GridStack options={{ cellHeight: 70, float: true }}>
<GridStackItem id="a" x={0} y={0} w={2} h={2}>
<h2>Widget A</h2>
<p>Drag and resize me!</p>
</GridStackItem>
<GridStackItem id="b" x={2} y={0} w={2} h={1}>
<h2>Widget B</h2>
</GridStackItem>
<GridStackItem id="c" x={4} y={0} w={2} h={3} autoPosition>
<h2>Widget C</h2>
</GridStackItem>
</GridStack>Dynamic Widgets
Use GridStackDynamic when widgets come from an array. It uses Svelte's {#each} with keyed blocks for proper lifecycle management.
<script lang="ts">
import { GridStackDynamic, GridStackItem } from 'svelte-gridstack';
let widgets = $state([
{ id: 'w1', x: 0, y: 0, w: 2, h: 2, title: 'Chart' },
{ id: 'w2', x: 2, y: 0, w: 3, h: 1, title: 'Stats' },
{ id: 'w3', x: 5, y: 0, w: 2, h: 3, title: 'Table' },
]);
function handleRemove(id: string) {
widgets = widgets.filter(w => w.id !== id);
}
</script>
<GridStackDynamic
{widgets}
options={{ cellHeight: 80, float: true }}
onchange={(nodes) => console.log('changed:', nodes)}
>
{#snippet children(widget, i)}
<GridStackItem id={widget.id} x={widget.x} y={widget.y} w={widget.w} h={widget.h}>
<div class="widget">
<h3>{widget.title}</h3>
<button onclick={() => handleRemove(widget.id)}>Remove</button>
</div>
</GridStackItem>
{/snippet}
</GridStackDynamic>API Reference
<GridStack>
Main grid container component.
Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| options | GridStackOptions | {} | GridStack configuration options |
Events (callback props):
| Prop | Type | Description |
|------|------|-------------|
| onchange | (nodes: GridStackNode[]) => void | Widgets changed position/size |
| onadded | (nodes: GridStackNode[]) => void | Widget(s) added |
| onremoved | (nodes: GridStackNode[]) => void | Widget(s) removed |
| ondragstart | (event, el, node) => void | Drag started |
| ondrag | (event, el, node) => void | Dragging |
| ondragstop | (event, el, node) => void | Drag stopped |
| onresizestart | (event, el, node) => void | Resize started |
| onresize | (event, el, node) => void | Resizing |
| onresizestop | (event, el, node) => void | Resize stopped |
| ondropped | (event, prevNode, newNode) => void | Widget dropped from another grid |
Imperative methods (via bind:this):
<script lang="ts">
import { GridStack } from 'svelte-gridstack';
let grid: GridStack;
</script>
<GridStack bind:this={grid} />
<button onclick={() => grid.save()}>Save Layout</button>
<button onclick={() => grid.compact()}>Compact</button>
<button onclick={() => grid.setStatic(true)}>Lock</button>Available methods: addWidget(), removeWidget(), update(), save(), load(), compact(), float(), column(), cellHeight(), enable(), disable(), setStatic(), batchUpdate(), removeAll(), destroy(), makeWidget(), getGridInstance().
<GridStackItem>
Individual widget wrapper. Must be a direct child of <GridStack> or <GridStackDynamic>.
Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| id | string | - | Widget ID |
| x | number | - | Column position |
| y | number | - | Row position |
| w | number | - | Width in columns |
| h | number | - | Height in rows |
| autoPosition | boolean | - | Auto-place the widget |
| minW / maxW | number | - | Min/max width |
| minH / maxH | number | - | Min/max height |
| noResize | boolean | - | Disable resize |
| noMove | boolean | - | Disable drag |
| locked | boolean | - | Lock in place |
| sizeToContent | boolean \| number | - | Auto-size to content |
All props are reactive - changing them will update the widget in the grid.
<GridStackDynamic>
Keyed-each grid container for data-driven widget lists.
Props:
| Prop | Type | Description |
|------|------|-------------|
| widgets | T[] | Array of widget data (must have id) |
| options | GridStackOptions | Grid options |
| children | Snippet<[T, number]> | Render snippet for each widget |
Supports the same event callbacks as <GridStack>.
Complete Options Example
<script lang="ts">
import { GridStack, GridStackItem } from 'svelte-gridstack';
import type { GridStackOptions } from 'svelte-gridstack';
const gridOpts: GridStackOptions = {
column: 12,
cellHeight: 'auto',
float: true,
animate: true,
margin: 10,
disableDrag: false,
disableResize: false,
minRow: 1,
staticGrid: false,
acceptWidgets: true,
};
let grid: GridStack;
function handleSave() {
const layout = grid.save();
localStorage.setItem('grid-layout', JSON.stringify(layout));
}
function handleLoad() {
const raw = localStorage.getItem('grid-layout');
if (raw) {
grid.load(JSON.parse(raw));
}
}
</script>
<button onclick={handleSave}>Save</button>
<button onclick={handleLoad}>Load</button>
<GridStack
bind:this={grid}
options={gridOpts}
onchange={(nodes) => console.log('Layout changed', nodes)}
ondragstop={(_, el, node) => console.log('Dropped', node)}
onresizestop={(_, el, node) => console.log('Resized', node)}
>
<GridStackItem id="chart" x={0} y={0} w={6} h={4} minW={3}>
<MyChart />
</GridStackItem>
<GridStackItem id="table" x={6} y={0} w={6} h={4}>
<MyTable />
</GridStackItem>
</GridStack>Notes
- You must import GridStack CSS yourself:
import 'gridstack/dist/gridstack.min.css' - All
GridStackOptionsandGridStackWidgettypes are re-exported fromgridstackfor convenience. - The library uses Svelte 5 runes (
$props,$state,$effect). Svelte 4 is not supported.
License
MIT
