Skip to Content
snapgrid is a react-grid-layout v2 alternative built on dnd-kit. Drag, resize, repack, and drag between grids.
DocumentationGuidesComponent layer

Component layer

snapgrid is headless-first, but you don’t have to wire the hooks yourself. The component layer is a thin shell in the react-grid-layout style over those exact hooks: <GridLayout> bundles the dnd-kit DragDropProvider, the container, the items, and the placeholder, so you never touch dnd-kit directly.

This is the escape hatch. If the headless provider/host/tile wiring is more than you want, reach for <GridLayout> — it mirrors react-grid-layout v2, so it’s the gentlest on-ramp. When one grid later needs custom markup, drop just that grid down to the hooks. Same engine; the two layers mix freely.

<GridLayout>

A controlled grid. Children are keyed by their layout item’s i; snapgrid positions each one and renders the resize handles and placeholder for you.

import { GridLayout, useContainerWidth, type Layout } from "@snapgridjs/react"; import { useState } from "react"; function Board() { const { width, containerRef } = useContainerWidth(); const [layout, setLayout] = useState<Layout>([ { i: "a", x: 0, y: 0, w: 4, h: 2 }, { i: "b", x: 4, y: 0, w: 4, h: 2 }, { i: "c", x: 8, y: 0, w: 4, h: 3 }, ]); return ( <div ref={containerRef}> <GridLayout layout={layout} width={width} onLayoutChange={setLayout} gridConfig={{ cols: 12, rowHeight: 60, margin: [12, 12] }} resizeConfig={{ handles: ["se", "e", "s"] }} > {layout.map((item) => ( <div key={item.i} className="tile"> {item.i} </div> ))} </GridLayout> </div> ); }

It supplies its own DragDropProvider; nest several (or wrap them in <SnapGridGroup>) and they share one.

Component layer
the turnkey <GridLayout> — no hooks, no dnd-kit wiring

Config props map 1:1 to the hook options

Every behaviour prop on <GridLayout> is the same option you’d pass to useGridContainer — only the spelling differs (a JSX prop vs. an object key). So each feature guide applies to both layers:

PropWhat it doesGuide
gridConfigColumns, row height, margins, padding, maxRows.Concepts
dragConfigHandle, cancel, threshold, bounds, snap.Dragging
resizeConfigWhich handles to show.Resizing
compactorPacking algorithm.Compaction
dropConfig / onDropAccept external draggables.External drop
isDraggable / isResizable / autoSizeGrid-level toggles.
onDragStartonResizeStopLifecycle callbacks.Events

See Components → GridLayout for the complete prop table.

<ResponsiveGridLayout>

The turnkey wrapper over useResponsiveLayout. Give it a per-breakpoint layouts map instead of a single layout; it switches column count and layout as width crosses breakpoints.

import { ResponsiveGridLayout, useContainerWidth, type ResponsiveLayouts } from "@snapgridjs/react"; const { width, containerRef } = useContainerWidth(); const [layouts, setLayouts] = useState<ResponsiveLayouts>({ lg: [/* … */] }); <div ref={containerRef}> <ResponsiveGridLayout width={width} layouts={layouts} onLayoutChange={(_active, all) => setLayouts(all)} > {["a", "b", "c"].map((id) => ( <div key={id} className="tile">{id}</div> ))} </ResponsiveGridLayout> </div>;

Cross-grid with components

Wrap sibling grids in <SnapGridGroup> — the shared provider — and tiles drag between them. (Nested <GridLayout>s share a provider automatically.)

<SnapGridGroup> <GridLayout layout={left} width={w} onLayoutChange={setLeft}> {left.map((it) => <div key={it.i} className="tile">{it.i}</div>)} </GridLayout> <GridLayout layout={right} width={w} onLayoutChange={setRight}> {right.map((it) => <div key={it.i} className="tile">{it.i}</div>)} </GridLayout> </SnapGridGroup>;

Item ids must be unique across all grids that share a provider — they share one drag manager.

Composing the pieces

The layer isn’t all-or-nothing. <GridItem> and <GridPlaceholder> are the same tiles <GridLayout> renders, exposed so you can drop them into a surface you host with useGridContainer — handy when you want the styled, memoized tile but control of the container:

const { containerProps, group } = useGridContainer({ layout, width, onLayoutChange }); <div {...containerProps}> {layout.map((it) => ( <GridItem key={it.i} id={it.i} group={group}> {it.i} </GridItem> ))} <GridPlaceholder group={group} /> </div>;

<GridItem> applies the stable .snapgrid-item class and data-grid-id / data-dragging attributes (see Styling) and is memoized, so re-rendering the surface doesn’t re-render every tile.

There’s no drag overlay to manage at either layer — a dragged tile floats itself. See Components for the full prop tables and Headless usage for the hooks.

Last updated on