Skip to content

About

Svelte 5 wrapper for GridStack.js

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

gridstack.svelte

A lightweight, modern Svelte 5 wrapper for GridStack.js v12 - create draggable, resizable dashboard layouts with ease.

📖 Overview

This library provides thin, performant Svelte 5 components that wrap GridStack.js v12, enabling you to build interactive dashboards with drag-and-drop functionality, responsive grids, and dynamic layouts. Built with Svelte's latest runes and snippets syntax for optimal performance.

✨ Features

  • 🎯 Svelte 5 Native - Built with modern runes ($state, $props, $bindable) and snippets
  • 🚀 Lightweight Wrapper - Minimal abstraction over GridStack.js for maximum performance
  • 📦 TypeScript Support - Full TypeScript definitions included
  • 🎨 Customizable - Complete access to GridStack.js options and methods
  • 📱 Responsive - Mobile-friendly with touch support
  • 🔧 Simple API - Easy to use with declarative Svelte components

⚡️ Quick Start

Installation

npm install gridstack.svelte
# or
pnpm add gridstack.svelte

Basic Usage

<script lang="ts">
	import Grid, { GridItem } from 'gridstack.svelte';
	import 'gridstack/dist/gridstack.min.css';
</script>

<Grid>
	<GridItem x={0} y={0} w={4} h={2}>Widget 1</GridItem>
	<GridItem x={4} y={0} w={4} h={4}>Widget 2</GridItem>
	<GridItem x={8} y={0} w={2} h={2}>Widget 3</GridItem>
</Grid>

Advanced Example

<script lang="ts">
	import Grid, { GridItem } from 'gridstack.svelte';
	import { GridStack } from 'gridstack';
	import 'gridstack/dist/gridstack.min.css';

	let grid = $state.snapshot(null);
	let options = $state({
		margin: 8,
		cellHeight: 70,
		acceptWidgets: true,
		removable: '#trash'
	});

	$effect(() => {
		// Setup drag-in from external elements
		GridStack.setupDragIn('.newWidget', undefined, [{ w: 2, h: 2, content: 'New Item' }]);
	});
</script>

<Grid ref={grid} {options}>
	<GridItem id="w1" x={0} y={0} w={4} h={2}>Dashboard Widget</GridItem>

	<GridItem id="w2" x={4} y={0} w={4} h={4} noResize={true} locked={true}>
		Locked Widget (Can't resize or move)
	</GridItem>

	<GridItem id="w3" x={8} y={0} w={2} h={2} noMove={true}>Can resize but not move</GridItem>
</Grid>

📚 API

Grid Component

The main container component that initializes GridStack.

Props:

  • ref - Bindable reference to the GridStack instance
  • options - GridStack configuration options (margin, cellHeight, etc.)
  • children - Snippet containing GridItem components

GridItem Component

Individual grid items that can be dragged and resized.

Props:

  • id - Unique identifier for the item
  • x, y - Grid position (0-based)
  • w, h - Width and height in grid units
  • minW, maxW - Min/max width constraints
  • minH, maxH - Min/max height constraints
  • noResize - Disable resizing
  • noMove - Disable moving
  • locked - Lock item (no resize, move, or drag)
  • autoPosition - Auto-position item if x/y not provided
  • children - Content to display inside the grid item

🎨 Styling Guide - Understanding GridStack's Structure

GridStack's Layer Structure

1. .grid-stack - The Container

  • Purpose: The overall grid container that manages the layout system
  • What it does: Sets up the coordinate system, handles grid calculations
  • Don't touch: Let GridStack manage this completely

2. .grid-stack-item - The Positioned Wrapper

  • Purpose: The absolutely positioned element that GridStack moves around
  • What it does:
    • Handles positioning (top, left, width, height)
    • Uses CSS variables for grid calculations (--gs-column-width, --gs-cell-height)
    • Contains resize handles as direct children
    • Manages all animations and transitions during drag/resize
  • Don't touch: Position, dimensions, padding, transforms, transitions
  • Never add: Any CSS that affects positioning or animation

3. .grid-stack-item-content - The Margin/Gap Manager

  • Purpose: Creates the visual gaps between widgets
  • What it does:
    • Uses position: absolute with top, right, bottom, left
    • These positions reference CSS variables (--gs-item-margin-*) to create gaps
    • Acts as a "negative space" creator
  • NEVER style this: Any styling breaks GridStack's margin system
  • Don't add: height: 100%, display: flex, backgrounds, borders, etc.

4. Your content - Direct child of .grid-stack-item-content

  • Purpose: Your actual widget content
  • What goes here: ALL your visual styling
    • Backgrounds, borders, shadows
    • Padding for internal spacing
    • Layout properties (flex, grid)
    • height: 100% to fill the available space
  • Best practice: Use a single wrapper div as the immediate child

The Critical Lesson

GridStack uses a "negative space" approach for margins:

  • The .grid-stack-item is the full cell size
  • The .grid-stack-item-content is positioned inside with offsets
  • The gap between items is created by these offsets, not by margins

This is why styling .grid-stack-item-content breaks everything - you're interfering with the positioning layer that creates the gaps.

Correct Structure

<div class="grid-stack">
	<!-- GridStack manages -->
	<div class="grid-stack-item">
		<!-- GridStack positions -->
		<div class="grid-stack-item-content">
			<!-- GridStack gaps - DON'T TOUCH -->
			<div class="your-widget">
				<!-- YOUR styling goes here -->
				<!-- Your content -->
			</div>
		</div>
	</div>
</div>

The mistake we made was trying to "help" GridStack by styling its internal structure, when we should have just styled our own content and let GridStack handle its layout mechanics.

🛠️ Development

# Install dependencies
pnpm install

# Run development server
pnpm dev

# Build library
pnpm build

# Run tests
pnpm test

# Type checking
pnpm check

📄 License

MIT

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

🔗 Links

About

Svelte 5 wrapper for GridStack.js

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages