Cross-grid dragging
Put two or more grids on one dnd-kit provider and tiles can be dragged between them. The
source grid loses the tile; the destination gains it. Each grid keeps its own controlled layout.
<SnapGridGroup> is that shared provider — it’s just dnd-kit’s DragDropProvider, named for intent
— so each grid hosts itself with useGridContainer, and a dragged tile floats itself across the seam
(no overlay to wire).
Cross-grid is the managed, grid-to-grid case. To drag between a grid and a non-grid dnd-kit sortable (a list, a kanban column), see dnd-kit interop.
import { SnapGridGroup, useGridContainer, useGridItem } from "@snapgridjs/react";
function Grid({ layout, width, onLayoutChange }) {
// Each grid hosts itself and returns its own `group`; the shared provider
// comes from the enclosing SnapGridGroup.
const { containerProps, group } = useGridContainer({ layout, width, onLayoutChange, gridConfig: cfg });
return (
<div {...containerProps}>
{layout.map((it) => (
<Tile key={it.i} id={it.i} group={group} />
))}
</div>
);
}
function Tile({ id, group }) {
const { ref, style } = useGridItem({ id, group });
return (
<div ref={ref} style={style} className="tile">
{id}
</div>
);
}
<SnapGridGroup>
<Grid layout={left} width={w} onLayoutChange={setLeft} />
<Grid layout={right} width={w} onLayoutChange={setRight} />
</SnapGridGroup>;How it works
Cross-grid dragging is just two grids on one dnd-kit DragDropProvider. <SnapGridGroup>
supplies that shared provider; a drag that starts in one grid is received by whichever grid
dnd-kit’s collision system resolves under the pointer. On drop:
- the source grid removes the tile and fires its
onLayoutChangewith the tile gone; - the destination grid inserts the tile at the drop cell and fires its
onLayoutChangewith the tile added.
Both updates happen in the same gesture, each against its own layout state.
Item ids must be unique across every grid in a group. They share one drag manager. If two
grids can hold an item with the same i, a cross-grid drop can’t tell them apart.
<SnapGridGroup> is exactly dnd-kit’s <DragDropProvider> — use whichever you like; grids under
one provider exchange tiles either way. Prefer the turnkey shell? Swap each <Grid> for a
<GridLayout> with keyed children inside the same group; it
bundles the container and items for you.
No overlay to wire. A dragged tile floats itself — it lifts into the top layer and crosses the gap between grids unclipped, carrying its own content with it. So the preview always shows the right tile, from whichever grid it came, with nothing extra to render.
Callbacks
The destination grid signals a received tile through onLayoutChange only. It does not fire
onDragStop, because it never fired onDragStart (the source grid owns the drag’s start/stop
pair). Wire cross-grid persistence to onLayoutChange on both grids.
Dropping outside any grid
If a tile is dropped outside every grid in the group, the move reverts. The source keeps it and no
onLayoutChange fires.
Want a grid inside a tile of another rather than side-by-side siblings? See Nested grids — they also cross levels by default, and you can give a sub-grid its own provider to keep it self-contained.