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

External drop

A grid can accept non-grid dnd-kit draggables — a widget palette, a sidebar of blocks — and turn a drop into a new layout item. Enable it with dropConfig and handle the result with onDrop.

External drop
drag a chip from the palette into the grid

Set it up

The grid and the external draggable must share one dnd-kit provider, so wrap both in a <SnapGridGroup> (dnd-kit’s DragDropProvider). The grid hosts itself with useGridContainer, opting in via dropConfig. The draggable is a plain useDraggable from @dnd-kit/react that carries a snapGridDrop payload describing the item to create.

import { Feedback } from "@dnd-kit/dom"; import { useDraggable } from "@dnd-kit/react"; import { SnapGridGroup, useGridContainer, useGridItem } from "@snapgridjs/react"; const CLONE = [Feedback.configure({ feedback: "clone" })]; function PaletteItem({ id, w, h }: { id: string; w: number; h: number }) { const { ref } = useDraggable({ id, data: { snapGridDrop: { w, h } }, plugins: CLONE }); return <button ref={ref}>{`${w}×${h}`}</button>; } function DropGrid({ layout, width, onLayoutChange }) { const { containerProps, group } = useGridContainer({ layout, width, onLayoutChange, onDrop: (next) => onLayoutChange(next), dropConfig: { enabled: true, defaultItem: { w: 2, h: 2 } }, }); 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> ); } function Board() { const [layout, setLayout] = useState<Layout>([]); return ( <SnapGridGroup> <PaletteItem id="pal-wide" w={4} h={1} /> <DropGrid layout={layout} width={width} onLayoutChange={setLayout} /> </SnapGridGroup> ); }

The drag preview

Each draggable is its own floating preview — there’s no shared overlay to wire up:

  • A grid tile being relocated (within or across grids) floats itself.
  • A palette item floats a clone of itself — that’s the CLONE plugin (Feedback.configure({ feedback: "clone" })) on its useDraggable above.

Why a clone? dnd-kit’s default feedback floats the element itself — right for a grid tile, but it would yank the palette item out of its tray. "clone" leaves the original in place and floats a copy instead. The copy is just your palette markup; it becomes a grid tile only once onDrop fires.

The snapGridDrop payload

The external draggable carries data.snapGridDrop to control the inserted item:

interface GridDropData { i?: string; // id for the new item; a unique one is generated if omitted w?: number; // width in columns h?: number; // height in rows }

If w/h are omitted, dropConfig.defaultItem is used (and finally 1×1, matching react-grid-layout’s default).

dropConfig options

OptionTypeDefaultDescription
enabledbooleanfalseAccept external draggables.
defaultItem{ w, h }{ w: 1, h: 1 }Size for a dropped item when the source omits snapGridDrop.
accept(source) => booleanaccept any non-grid draggableRestrict which sources are accepted.

onDrop, not onLayoutChange

An external drop fires onDrop(layout, item, event) — the next layout, the synthesized item, and the source event — rather than onLayoutChange. That lets you add the item to both your layout state and any side data keyed by id (titles, content, etc.) in one place.

Synthesized ids are prefixed with the grid’s id, so two drop-enabled grids in a group never mint the same id.

Beyond one-way drops

External drop is a one-way drop of a plain dnd-kit Draggable into a grid. For two-way movement with a useSortable list or board — cards in, tiles back out, both reordering live — see dnd-kit interop.

Last updated on