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:
| Element | Class | Notes |
|---|---|---|
| Surface | .snapgrid | className prop is appended. |
| Item | .snapgrid-item | data-grid-id, data-dragging attributes. |
| Resize handle | .snapgrid-resize-handle, .snapgrid-resize-handle--{axis} | One per configured handle. |
| Placeholder | .snapgrid-placeholder | Landing-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’si, 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 fromcontainerProps).
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 drivetransform, so atransform: scale(...)in[data-dragging]is overridden mid-drag. To scale or rotate the float, use a dnd-kit modifier instead.cursor— the floating tile ispointer-events: nonewhile dragging, so set the grab cursor on the tile (or itshandleRef) 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.