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 });| Type | Description | |
|---|---|---|
options.initialWidth | number | Width before measurement. Default 1280. |
→ width | number | Measured width (or initialWidth before mount). |
→ mounted | boolean | true once measured at least once. |
→ containerRef | (el: HTMLElement | null) => void | Attach 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).
| Option | Type | Default | Description |
|---|---|---|---|
layout | Layout | — | Required. Controlled array of items. Never mutated. |
width | number | — | Required. Container width in px (from useContainerWidth). |
onLayoutChange | (layout: Layout) => void | — | Called with the next layout when an interaction commits. The grid is controlled — apply it, or nothing moves. |
gridConfig | Partial<GridConfig> | see GridConfig | Columns, row height, margins, padding, maxRows. Unset fields use defaults. |
dragConfig | DragConfig | — | Drag behaviour: handle (CSS selector — headless prefers handleRef), cancel, threshold, bounded, snapToGrid. |
resizeConfig | ResizeConfig | { handles: ["se"] } | Resize behaviour: which handle axes are allowed. |
dropConfig | DropConfig | { enabled: false } | Accept external draggables: enabled, defaultItem, accept. |
accept | (source) => boolean | — | dnd-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. |
compactor | Compactor | verticalCompactor | Packing algorithm. Swap at runtime. |
isDraggable | boolean | true | Grid-level drag toggle (overrides per-item when false). |
isResizable | boolean | true | Grid-level resize toggle. |
autoSize | boolean | true | Grow the surface height to fit content. |
id | string | auto (useId) | Stable id for the droppable surface; surfaced back as group. |
type | string | "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 onDragStop | GridEventCallback | — | Drag lifecycle (layout, oldItem, newItem, placeholder, event, node). |
onResizeStart onResize onResizeStop | GridEventCallback | — | Resize lifecycle, same signature. |
onDrop | (layout, item, event) => void | — | Fired when an external draggable is dropped in — instead of onLayoutChange. |
Returns (UseGridContainerResult)
| Field | Type | Description |
|---|---|---|
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. |
group | string | This grid’s id (the id option, or a generated one). Pass it to every item-level hook. |
isDropTarget | boolean | true while a compatible draggable is over the grid — handy for highlighting. |
controller | GridController | The 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>;| Option | Type | Default | Description |
|---|---|---|---|
id | string | — | Required. Matches the layout item’s i. |
group | string | — | Required. The owning grid’s id (from its useGridContainer). |
type | string | "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. |
| → | Type | Description |
|---|---|---|
ref | (el: Element | null) => void | Attach to the tile element. |
handleRef | (el: Element | null) => void | Optional drag handle — attach to a child to restrict pointer dragging to it. The headless form of dragConfig.handle. |
style | CSSProperties | position: 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. |
isDragging | boolean | True while this tile is the active drag source. |
item | LayoutItem | undefined | The 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) => {},
});| Option | Type | Description |
|---|---|---|
width | number | Current container width. |
layouts | ResponsiveLayouts | Per-breakpoint layout map. |
breakpoints | Breakpoints | Optional; defaults to DEFAULT_BREAKPOINTS. |
cols | BreakpointCols | Optional; defaults to DEFAULT_BREAKPOINT_COLS. |
compactor | Compactor | Used when generating a missing breakpoint’s layout. |
onLayoutChange | (layout, layouts) => void | Active layout + updated map. |
onBreakpointChange | (breakpoint, cols) => void | On 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.