a dnd-kit grid · react-grid-layout alternative
Grids that drag, resize, and repack.
A headless-first grid layout built on dnd-kit — draggable, resizable, repacking tiles that compose with the sortables and droppables you already have. A component layer makes react-grid-layout users feel right at home too.
Why snapgrid
Headless-first, dnd-kit-native
Hooks you wire to your own markup, under your dnd-kit provider — tiles declare a group, like useSortable. <GridLayout> is the turnkey shell on top.
Learn more →Controlled & predictable
You own the layout array. Every drag, resize, and cross-grid move comes back through onLayoutChange. No hidden internal state.
Learn more →Pluggable packing
Vertical, horizontal, or free. Plus masonry, gravity, and shelf packers, or bring your own Compactor.
Learn more →Cross-grid & dnd-kit interop
Drag tiles between grids, or between a grid and a dnd-kit sortable list or board — cards land at a real cell, tiles drop back out, all under one provider.
Learn more →Nested grids
Drop a grid inside a tile of another and drag tiles between levels — or give a sub-grid its own provider to keep it contained.
Learn more →Responsive
Per-breakpoint layouts via the useResponsiveLayout hook, or the turnkey <ResponsiveGridLayout>.
Learn more →Resizable, with limits
Any edge or corner, with per-item minW/maxW/minH/maxH honored and static tiles that never move.
Learn more →Keyboard accessible
Every tile is keyboard-draggable: focus, pick up with Enter, move with the arrow keys, drop or cancel with Escape. No extra wiring.
Learn more →A 30-second example
import { DragDropProvider } from "@dnd-kit/react";
import { useContainerWidth, useGridContainer, useGridItem } from "@snapgridjs/react";
import { useState } from "react";
function Board() {
const { width, containerRef } = useContainerWidth();
const [layout, setLayout] = useState([
{ 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: 2 },
]);
// DragDropProvider is the outermost element; the grid host (Surface) sits
// inside it, so useGridContainer resolves the provider's dnd-kit manager. A
// dragged tile floats itself, so there's no overlay to render.
return (
<div ref={containerRef}>
<DragDropProvider>
<Surface layout={layout} width={width} onLayoutChange={setLayout} />
</DragDropProvider>
</div>
);
}
function Surface({ layout, width, onLayoutChange }) {
// useGridContainer is the grid host; it returns the grid's `group`.
const { containerProps, group } = useGridContainer({ layout, width, onLayoutChange });
return (
<div {...containerProps}>
{layout.map((it) => <Tile key={it.i} id={it.i} group={group} />)}
</div>
);
}
function Tile({ id, group }) {
// each tile resolves its grid by `group` — like a dnd-kit sortable
const { ref, style } = useGridItem({ id, group });
return <div ref={ref} style={style} className="tile">{id}</div>;
}Coming from react-grid-layout?
The <GridLayout> component layer mirrors react-grid-layout v2's model — same controlled layout, onLayoutChange, and config objects — so RGL users adopt it quickly, then drop any grid down to the headless hooks at their own pace. Same engine, no second migration.
import ReactGridLayout, { useContainerWidth, verticalCompactor } from "react-grid-layout";
import "react-grid-layout/css/styles.css";
import { GridLayout, useContainerWidth, verticalCompactor } from "@snapgridjs/react";
function Board() {
const { width, containerRef, mounted } = useContainerWidth();
return (
<div ref={containerRef}>
{mounted && (
<ReactGridLayout
<GridLayout
width={width}
layout={layout}
gridConfig={{ cols: 12, rowHeight: 30 }}
dragConfig={{ enabled: true, handle: ".handle" }}
compactor={verticalCompactor}
>
{children}
</ReactGridLayout>
</GridLayout>
)}
</div>
);
}Migration path
- Not a literal drop-in. Same API shape, but you restyle — snapgrid ships no CSS and uses its own class names.
- v2 hooks aren't mirrored.
useGridLayout/useResponsiveLayoutcode moves to the headless API. - On v1? No one-import shim — its
WidthProvider/ flat-prop API is dated enough that it's worth modernising. The migration guide maps it prop by prop.