| [ Web Proxy ] |
| Viewing: https://snapgrid.dev | [Back] [Original] |
a dnd-kit grid react-grid-layout alternative
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.
pnpm add @snapgridjs/react @dnd-kit/react @dnd-kit/domHooks you wire to your own markup, under your dnd-kit provider tiles declare a group, like a sortable. <GridLayout> is the turnkey shell on top.
Learn moreYou own the layout array. Every drag, resize, and cross-grid move comes back through onLayoutChange. No hidden internal state.
Learn moreVertical, horizontal, or free. Plus masonry, gravity, and shelf packers, or bring your own Compactor.
Learn moreDrag 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 moreDrop 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 morePer-breakpoint layouts via the useResponsiveLayout hook, or the turnkey <ResponsiveGridLayout>.
Learn moreAny edge or corner, with per-item minW/maxW/minH/maxH honored and static tiles that never move.
Learn moreEvery tile is keyboard-draggable: focus, pick up with Enter, move with the arrow keys, drop or cancel with Escape. No extra wiring.
Learn moreimport { 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>;
}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
useGridLayout / useResponsiveLayout code moves to the headless API.WidthProvider / flat-prop API is dated enough that it's worth modernising. The migration guide maps it prop by prop.| Web Proxy Viewer | New URL | Original Page |