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

Hooks

snapgrid’s primary API. Each returns refs, styles, and state to spread onto your own elements. See Headless usage for the full picture.

Headless grids render inside a dnd-kit DragDropProvider that you supply. useGridContainer is the grid host and returns the grid’s group; the item-level hooks take that group to resolve their grid.

useContainerWidth

Measures a container’s width with a ResizeObserver (SSR-safe). Replaces react-grid-layout’s WidthProvider.

const { width, mounted, containerRef } = useContainerWidth({ initialWidth: 1024 });
TypeDescription
options.initialWidthnumberWidth before measurement. Default 1280.
widthnumberMeasured width (or initialWidth before mount).
mountedbooleantrue once measured at least once.
containerRef(el: HTMLElement | null) => voidAttach to the element to measure.

useGridContainer

The grid host: takes the controlled layout + config, owns the grid’s drag/resize session, registers the droppable surface, and returns props for your container plus 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> );

Options (UseGridControllerOptions)

The controlled state plus the per-grid config. These are exactly <GridLayout>’s props minus className / style (here you own the surface element, so you style it directly).

OptionTypeDefaultDescription
layoutLayoutRequired. Controlled array of items. Never mutated.
widthnumberRequired. Container width in px (from useContainerWidth).
onLayoutChange(layout: Layout) => voidCalled with the next layout when an interaction commits. The grid is controlled — apply it, or nothing moves.
gridConfigPartial<GridConfig>see GridConfigColumns, row height, margins, padding, maxRows. Unset fields use defaults.
dragConfigDragConfigDrag behaviour: handle (CSS selector — headless prefers handleRef), cancel, threshold, bounded, snapToGrid.
resizeConfigResizeConfig{ handles: ["se"] }Resize behaviour: which handle axes are allowed.
dropConfigDropConfig{ enabled: false }Accept external draggables: enabled, defaultItem, accept.
accept(source) => booleandnd-kit interop: also accept a foreign sortable (e.g. a tray card) as a drop target. You drive the receive yourself in onDragOver with snapMove.
compactorCompactorverticalCompactorPacking algorithm. Swap at runtime.
isDraggablebooleantrueGrid-level drag toggle (overrides per-item when false).
isResizablebooleantrueGrid-level resize toggle.
autoSizebooleantrueGrow the surface height to fit content.
idstringauto (useId)Stable id for the droppable surface; surfaced back as group.
typestring"grid"The droppable surface’s dnd-kit type. Override to namespace grids for interop (branch onDragOver on it, or have a foreign draggable accept only this grid). Grids resolve by id, so any value still receives tiles + cross-grid drops.
onDragStart onDrag onDragStopGridEventCallbackDrag lifecycle (layout, oldItem, newItem, placeholder, event, node).
onResizeStart onResize onResizeStopGridEventCallbackResize lifecycle, same signature.
onDrop(layout, item, event) => voidFired when an external draggable is dropped in — instead of onLayoutChange.

Returns (UseGridContainerResult)

FieldTypeDescription
containerProps{ ref, style, "data-drop-target"? }Spread onto your surface element. style is position: relative + width + auto-sized height; data-drop-target is present while a compatible draggable is over the grid.
groupstringThis grid’s id (the id option, or a generated one). Pass it to every item-level hook.
isDropTargetbooleantrue while a compatible draggable is over the grid — handy for highlighting.
controllerGridControllerThe live store, for advanced composition.

useGridItem

Wires a single tile: drag activation, positioning style, and drag state. Takes an options object, mirroring useSortable: id, group (the value its useGridContainer returned), and an optional type.

const { ref, style, isDragging, item } = useGridItem({ id, group }); return <div ref={ref} style={style}>{item?.i}</div>;
OptionTypeDefaultDescription
idstringRequired. Matches the layout item’s i.
groupstringRequired. The owning grid’s id (from its useGridContainer).
typestring"grid-item"The dnd-kit sortable type the tile carries. Override to namespace tiles for interop — e.g. so a foreign sortable list accepts only one grid’s tiles. The grid identifies its own tiles by their payload, not this string, so any value still drags and crosses grids.
TypeDescription
ref(el: Element | null) => voidAttach to the tile element.
handleRef(el: Element | null) => voidOptional drag handle — attach to a child to restrict pointer dragging to it. The headless form of dragConfig.handle.
styleCSSPropertiesposition: absolute + left/top + size. Reflow animates on the compositor (a transform FLIP), so the dnd-kit float reads the tile’s true rect during ecosystem hand-offs.
isDraggingbooleanTrue while this tile is the active drag source.
itemLayoutItem | undefinedThe tile’s current (possibly reflowed) entry.

useGridResizeHandle

Models a resize handle as its own draggable. Position and style it however you like; group is the grid’s id.

const { ref, handleProps, isResizing } = useGridResizeHandle({ id: itemId, handle: "se", group }); return <span ref={ref} {...handleProps} />;

handleProps carries the marker attribute that excludes the handle from item-drag activation.

useGridPlaceholder

Returns where the landing-cell marker should render, or null when idle. group is the grid’s id.

const placeholder = useGridPlaceholder(group); {placeholder && <div style={placeholder.style} />}

useResponsiveLayout

The headless engine behind ResponsiveGridLayout. Resolves the active breakpoint, columns, and layout from the container width.

const { breakpoint, cols, layout, onLayoutChange } = useResponsiveLayout({ width, layouts, onLayoutChange: (active, all) => setLayouts(all), onBreakpointChange: (bp, cols) => {}, });
OptionTypeDescription
widthnumberCurrent container width.
layoutsResponsiveLayoutsPer-breakpoint layout map.
breakpointsBreakpointsOptional; defaults to DEFAULT_BREAKPOINTS.
colsBreakpointColsOptional; defaults to DEFAULT_BREAKPOINT_COLS.
compactorCompactorUsed when generating a missing breakpoint’s layout.
onLayoutChange(layout, layouts) => voidActive layout + updated map.
onBreakpointChange(breakpoint, cols) => voidOn breakpoint change.

Returns the active breakpoint, its cols, the resolved layout, and an onLayoutChange to pass to the grid (it updates the active breakpoint’s entry).

No overlay hook (tiles float themselves)

There’s no useGridDragOverlay — and no overlay component either. While you drag with a pointer, dnd-kit lifts the active tile into the browser’s top layer, so the tile you already rendered floats itself above everything and crosses between grids unclipped; on drop it settles into its new cell. Nothing extra to wire. Style the floating state through the tile’s own isDragging. See The dragged tile floats itself.

If you need a separate floating element — a clone, a ghost, different markup — dnd-kit’s DragOverlay is re-exported from @snapgridjs/react. It’s a component, not a hook, because it owns and renders its own element, so there’s nothing for a hook to return.

Last updated on