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

Styling

snapgrid ships no CSS. The headless hooks hand you inline positioning styles plus drag/resize state; you bring every class name and the entire look. The component layer adds a few stable class names and data attributes on top, so you have predictable hooks to target when you use <GridLayout>.

Styling headless tiles

useGridItem returns the positioning style and isDragging; nothing else is imposed. You choose the element, the class names, and which state to reflect in the DOM:

function Tile({ id, group }) { const { ref, style, isDragging } = useGridItem({ id, group }); return ( <div ref={ref} style={style} className="tile" data-dragging={isDragging || undefined}> {id} </div> ); }
.tile { border-radius: 10px; background: var(--card); box-shadow: 0 1px 2px rgb(0 0 0 / 0.06); } /* The tile floats itself, so [data-dragging] styles the element you're dragging around — give it a "lifted" cue. (Don't touch left/top/transform; the hook owns positioning and reflow motion.) */ .tile[data-dragging] { box-shadow: 0 12px 28px rgb(0 0 0 / 0.22); }

The grid surface from useGridContainer carries a data-drop-target attribute while a compatible draggable is over it, so you can highlight the drop zone:

.my-surface[data-drop-target] { outline: 2px dashed var(--accent); }

Positioning styles are inline

The style from useGridItem positions the tile with position: absolute + left / top (sized with width / height). useGridContainer’s style sets position: relative + the surface size. Spread them as-is and don’t override these properties.

You don’t add a reflow transition yourself — the hook animates a tile sliding to its new cell on the compositor (a transform FLIP) during a drag and on out-of-drag layout changes. (Tiles rest on left/top rather than a transform so dnd-kit’s float reads each tile’s true rect when one is handed off to a sortable list — see dnd-kit interop.) Add your own visual properties alongside the spread; just leave left / top / transform / transition to the hook.

Component-layer class names

When you use the turnkey <GridLayout>, it (and <GridItem> / <GridPlaceholder>) applies these stable hooks for you — the headless hooks apply none of them:

ElementClassNotes
Surface.snapgridclassName prop is appended.
Item.snapgrid-itemdata-grid-id, data-dragging attributes.
Resize handle.snapgrid-resize-handle, .snapgrid-resize-handle--{axis}One per configured handle.
Placeholder.snapgrid-placeholderLanding-cell marker.
.snapgrid-item { border-radius: 10px; background: var(--card); box-shadow: 0 1px 2px rgb(0 0 0 / 0.06); } .snapgrid-item[data-dragging] { box-shadow: 0 12px 28px rgb(0 0 0 / 0.22); } .snapgrid-placeholder { background: rgb(99 102 241 / 0.15); border: 1px dashed rgb(99 102 241 / 0.6); border-radius: 8px; }
  • data-grid-id: the item’s i, handy for selectors and tests.
  • data-dragging: present on the active tile while dragging.
  • data-drop-target: present on the container while a compatible draggable is over it (this one is on the headless surface too — it comes from containerProps).

Custom placeholder

Render your own placeholder with the headless hook’s returned style (group is the grid’s id from useGridContainer):

const placeholder = useGridPlaceholder(group); {placeholder && <div className="my-ghost" style={placeholder.style} />}

The convenience <GridPlaceholder group={group}> ships a subtle default look you can override with className or style. For anything more, call useGridPlaceholder(group) directly.

Styling the floating tile

While you drag with a pointer, the tile floats itself in the browser’s top layer — there’s no separate overlay element. So [data-dragging] styles the tile you’re dragging around, not a ghost left behind in the grid. Use it for a lift cue — a deeper shadow, a ring, a touch of brightness:

.tile[data-dragging] { box-shadow: 0 12px 28px rgb(0 0 0 / 0.22); }

Two properties are off-limits on the floating tile:

  • transform / translate / scale — the hook’s reflow FLIP and dnd-kit’s float both drive transform, so a transform: scale(...) in [data-dragging] is overridden mid-drag. To scale or rotate the float, use a dnd-kit modifier  instead.
  • cursor — the floating tile is pointer-events: none while dragging, so set the grab cursor on the tile (or its handleRef) at rest / :active, not via [data-dragging].

For the “where it’ll land” affordance, render the landing-cell placeholder — that marker tracks the target cell as you drag. (dnd-kit separately drops an invisible [data-dnd-placeholder] clone at the drag origin to hold space; it’s visibility: hidden, and since tiles are absolutely positioned there’s nothing there for you to style.)

For a separate floating preview — a clone, a ghost, or different markup — use dnd-kit’s raw <DragOverlay> (re-exported from @snapgridjs/react) and pin it out of flow yourself:

The raw <DragOverlay> needs a fixed base. It’s a plain <div data-dnd-overlay> that only gets position: fixed from a stylesheet dnd-kit injects while dragging. In the frame around drop, before that applies, an overlay rendered inside (or next to) your grid can reflow as a full-width block at the bottom of the grid — a one-frame flash. Pin it in CSS so it can’t:

[data-dnd-overlay] { position: fixed; top: 0; left: 0; pointer-events: none; }

dnd-kit’s own !important rules still drive the live position during a real drag.

Last updated on