# Callbacks (https://dnd-grid.blode.md/api-reference/callbacks) ## Overview dnd-grid provides callbacks for drag, resize, drop, and layout changes. ## Callback signature Drag and resize callbacks receive an event object: ```tsx type GridDragEvent = { type: "dragStart" | "drag" | "dragEnd"; layout: Layout; previousItem: LayoutItem | null | undefined; item: LayoutItem | null | undefined; placeholder: LayoutItem | null | undefined; event: Event; node: HTMLElement | null | undefined; }; type GridResizeEvent = { type: "resizeStart" | "resize" | "resizeEnd"; layout: Layout; previousItem: LayoutItem | null | undefined; item: LayoutItem | null | undefined; placeholder: LayoutItem | null | undefined; event: Event; node: HTMLElement | null | undefined; handle: ResizeHandleAxis; }; ``` ## Layout change Fires whenever the layout changes. Use this to persist layout state. ## Drag callbacks Fires when a drag operation begins. Fires continuously during drag. Fires when a drag operation ends. ## Resize callbacks Fires when a resize operation begins. Fires continuously during resize. Fires when a resize operation ends. ## Drop callbacks Fires when an external item is dropped onto the grid. Called when an external item is dragged over the grid. Return size or `false` to reject the drop. ## Performance > [!WARNING] > `onDrag` and `onResize` fire frequently. Avoid expensive work in these callbacks. For expensive updates, prefer `onDragEnd` or `onResizeEnd`, or set `callbackThrottle` on `DndGrid` to limit call frequency. # DndGrid (https://dnd-grid.blode.md/api-reference/dnd-grid) ## Import ```tsx import { DndGrid } from "@dnd-grid/react"; ``` > [!NOTE] > Need a headless wrapper? Use [`useDndGrid`](https://dnd-grid.blode.md/hooks/use-dnd-grid) to build your > own container while keeping the same drag/resize behavior. > For breakpoint layouts, use `ResponsiveDndGrid`. > If you already have a measured width, pass `width`. ## Props ### Core props Grid layout state (position and size per item). Missing entries are derived from children (with a dev warning). Grid items; each child `key` must match a layout `id`. ### Measurement Optional container width in pixels. When provided, measurement is skipped. Delay rendering until the container width is measured. Width used before measurement when rendering early. Props applied to the measurement wrapper. ### Grid configuration Number of columns. Row height in pixels. Gap between items. Use a number or `{ top, right, bottom, left }`. Container padding. Use a number or `{ top, right, bottom, left }`. If null, uses the gap value. Resize container height to fit content. Maximum rows. ### Behaviour Enable dragging for all items. Can be overridden per item. Enable resizing for all items. Can be overridden per item. Auto-scroll near scroll edges. Use `true` for defaults or pass options. Keep items within bounds during drag. Compaction strategy and collision behaviour. Drag and resize constraints. Defaults to `defaultConstraints`. Validate layouts at runtime with Zod. Disable to avoid extra work in hot paths. Throttle `onDrag` and `onResize` callbacks in milliseconds. Pass a number to apply to both, or an object for per-callback control. ### Motion Configure spring and shadow animations for drag, resize, and settling. Motion preference override. Use `"system"` to respect `prefers-reduced-motion`. ### Accessibility Configure aria-live announcements for drag, resize, and focus. Pass `false` to disable. Accessible label for the grid container. ID of the element that labels the grid. ID of the element that describes the grid. ### Drag configuration Selector for elements that start a drag. Selector for elements that cancel a drag. Touch delay in milliseconds. ### Resize configuration Edges and corners that show resize handles. Custom resize handle component. ### Drop configuration Drop behavior is enabled when you provide `onDrop` or `onDropDragOver`. Defaults for the dropped item. ### Transform Scale factor for CSS transforms. ### Styling Extra CSS class for the container. Inline styles for the container. Customize className and style for items, placeholders, and resize handles. Ref to the container element. ## Callbacks | Callback | Description | | -------- | ----------- | | `onLayoutChange` | Fires when the layout changes. | | `onDragStart` | Fires when dragging starts. | | `onDrag` | Fires while dragging. | | `onDragEnd` | Fires when dragging ends. | | `onResizeStart` | Fires when resizing starts. | | `onResize` | Fires while resizing. | | `onResizeEnd` | Fires when resizing ends. | | `onDrop` | Fires when an external item is dropped. | | `onDropDragOver` | Fires when an external item is dragged over the grid. | # Layout item (https://dnd-grid.blode.md/api-reference/layout-item) ## Layout type `Layout` is an array of `LayoutItem` objects: ```tsx import { type Layout } from "@dnd-grid/react"; type CardData = { kind: "chart" | "text" }; const layout: Layout = [ { id: "a", x: 0, y: 0, w: 2, h: 2, data: { kind: "chart" } }, { id: "b", x: 2, y: 0, w: 2, h: 2, data: { kind: "text" } }, ]; ``` ## Properties ### Required Unique id. Must match the child's `key`. X position in grid units (0-based). Y position in grid units (0-based). Width in grid units. Height in grid units. ### Optional Minimum width. Maximum width. Minimum height. Maximum height. Per-item drag and resize constraints. If true, the item is locked. Override grid drag setting. Override grid resize setting. Override grid bounded setting. Override grid resize handles. Optional typed metadata for the item. ## Examples ### With size constraints ```tsx const layout: Layout = [ { id: "constrained", x: 0, y: 0, w: 4, h: 3, minW: 2, maxW: 8, minH: 2, maxH: 6, }, ]; ``` # API overview (https://dnd-grid.blode.md/api-reference/overview) ## Exports Common exports: ```tsx import { DndGrid, ResponsiveDndGrid, GridItem, ResizeHandle, useContainerWidth, useDndGrid, useDndGridResponsiveLayout, useDndGridItemState, useOptionalDndGridItemState, useReducedMotion, useEdgeScroll, createLayoutEngine, verticalCompactor, defaultConstraints, layoutSchema, layoutItemSchema, validateLayout, type Compactor, type GridDragEvent, type GridResizeEvent, type LayoutConstraint, type Layout, type LayoutItem, type ResponsiveLayouts, } from "@dnd-grid/react"; ``` ## Components | Component | Description | |-----------|-------------| | [`DndGrid`](https://dnd-grid.blode.md/api-reference/dnd-grid) | Main grid layout component | | [`ResponsiveDndGrid`](https://dnd-grid.blode.md/api-reference/responsive-dnd-grid) | Breakpoint-aware responsive grid | Most apps should start with `DndGrid` for single-layout grids and `ResponsiveDndGrid` for breakpoint layouts. Use [`useDndGrid`](https://dnd-grid.blode.md/hooks/use-dnd-grid) when you need a custom wrapper or want to control rendering. Both components measure their container width automatically (or accept a `width` prop when you already know it). Also exported: `GridItem` and `ResizeHandle`. ## Hooks | Hook | Description | |------|-------------| | [`useContainerWidth`](https://dnd-grid.blode.md/hooks/use-container-width) | Measure container width with ResizeObserver | | [`useDndGrid`](https://dnd-grid.blode.md/hooks/use-dnd-grid) | Headless hook for building custom grid wrappers | | [`useDndGridResponsiveLayout`](https://dnd-grid.blode.md/hooks/use-dnd-grid-responsive-layout) | Manage responsive breakpoints + layout state | | [`useDndGridItemState`](https://dnd-grid.blode.md/hooks/use-dnd-grid-item-state) | Access item layout and interaction state | | [`useEdgeScroll`](https://dnd-grid.blode.md/hooks/use-edge-scroll) | Auto-scroll while dragging near scroll edges | ## Types | Type | Description | |------|-------------| | [`Layout`](https://dnd-grid.blode.md/api-reference/layout-item#layout) | Array of layout items (`LayoutItem[]`) | | [`LayoutItem`](https://dnd-grid.blode.md/api-reference/layout-item) | Position and size of a single grid item | | `ResponsiveLayouts` | Breakpoint → layout map | | `Compactor` | Pluggable compaction strategy | | `LayoutConstraint` | Drag/resize constraint definition | | `GridDragEvent` | Event object for drag callbacks | | `GridResizeEvent` | Event object for resize callbacks | ## Additional exports `@dnd-grid/react` also exports: - `useOptionalDndGridItemState` and `useReducedMotion` - `createLayoutEngine` and layout engine types for headless usage ## Runtime validation `layoutSchema`, `layoutItemSchema`, and `validateLayout` use Zod to validate layouts at runtime. `DndGrid` and `useDndGrid` also expose a `validation` prop (default: enabled in dev, disabled in production) to fail fast on bad layouts. Validation adds runtime work and bundle weight, so keep it off in hot paths if you do not need it. ## CSS classes Common classes you can target: | Class | Element | |-------|---------| | `.dnd-grid` | Grid container | | `.dnd-grid-item` | Each grid item | | `.dnd-grid-item-content` | Non-placeholder grid items | | `.dnd-grid-placeholder` | Placeholder during drag/resize | | `.dnd-draggable-dragging` | Item currently being dragged | | `.resizing` | Item currently being resized | ## Quick reference ### Essential props ```tsx {}} > {children} ``` ```tsx {children} ``` ## Import styles Import the required CSS: ```tsx import "@dnd-grid/react/styles.css"; ``` # ResponsiveDndGrid (https://dnd-grid.blode.md/api-reference/responsive-dnd-grid) ## Import ```tsx import { ResponsiveDndGrid } from "@dnd-grid/react"; ``` > [!NOTE] > Use `ResponsiveDndGrid` when you need breakpoint layouts. For a single layout, > use [`DndGrid`](https://dnd-grid.blode.md/api-reference/dnd-grid). If you already have a measured > width, pass `width` directly. ## Props ### Core props Breakpoint → layout map. Missing layouts are derived from the nearest larger breakpoint (default behavior). Grid items; each child `key` must match a layout item `id`. ### Measurement Optional container width in pixels. When provided, measurement is skipped. Delay rendering until the container width is measured. Width value used before measuring when rendering early. Props applied to the measurement wrapper. ### Responsive configuration Breakpoint map, defaults to `lg`, `md`, `sm`, `xs`, `xxs`. Column count per breakpoint. Gap per breakpoint (number or spacing object). Padding per breakpoint (or `null` to use gap). Uncontrolled initial layouts per breakpoint. Compaction strategy used for layout generation and collisions. Control how missing breakpoint layouts are handled. Fires when the active breakpoint changes. Fires when any breakpoint layout changes. Fires when the active breakpoint layout changes. Fires on width or breakpoint configuration changes. ### Grid configuration & behavior `ResponsiveDndGrid` supports the same grid configuration, behavior, accessibility, and styling props as [`DndGrid`](https://dnd-grid.blode.md/api-reference/dnd-grid) (e.g. `rowHeight`, `draggable`, `resizable`, `autoSize`, `slotProps`, and callbacks). # Compactors (https://dnd-grid.blode.md/concepts/compactors) Compactors are pluggable strategies that determine how items pack when space opens up and how collisions are resolved. Pass a `Compactor` to the `compactor` prop on `DndGrid`, or use them with responsive layouts. ## Built-in compactors | Compactor | Behavior | |-----------|----------| | `verticalCompactor` | Default. Packs items upward by row. | | `horizontalCompactor` | Packs items left-to-right, wrapping to the next row. | | `noCompactor` | Leaves items where they are, no packing. | | `verticalOverlapCompactor` | Vertical mode but allows overlap. | | `horizontalOverlapCompactor` | Horizontal mode but allows overlap. | | `fastVerticalCompactor` | Faster vertical packing for large layouts. | | `fastHorizontalCompactor` | Faster horizontal packing for large layouts. | | `fastVerticalOverlapCompactor` | Fast vertical mode with overlap allowed. | | `fastHorizontalOverlapCompactor` | Fast horizontal mode with overlap allowed. | ```tsx import { verticalCompactor, horizontalCompactor, noCompactor, fastVerticalCompactor, } from "@dnd-grid/react"; ``` ## Overlap and collision behavior - `allowOverlap: true` skips collision resolution. Items can overlap freely. - `preventCollision: true` blocks a move if it would collide with another item. ```tsx import { verticalCompactor, verticalOverlapCompactor } from "@dnd-grid/react"; ``` ## Helpers and custom compactors A compactor has four fields: `type`, `allowOverlap`, `compact`, and `onMove`. You can implement your own, or build on the helpers below: - `compactItemVertical` - `compactItemHorizontal` - `resolveCompactionCollision` ```tsx import { type Compactor, compactItemVertical } from "@dnd-grid/react"; const myCompactor: Compactor = { type: "vertical", allowOverlap: false, compact(layout, _cols) { // Reorder items by your own logic, then compact vertically. const sorted = [...layout]; const out = sorted.map((item) => ({ ...item })); const compareWith = out.filter((item) => item.static); let maxY = 0; for (const item of out) { if (!item.static) { compactItemVertical(compareWith, item, out, maxY); maxY = Math.max(maxY, item.y + item.h); compareWith.push(item); } } return out; }, onMove(layout, item, x, y) { return layout.map((entry) => entry.id === item.id ? { ...entry, x, y, moved: true } : entry, ); }, }; ``` # Constraints (https://dnd-grid.blode.md/concepts/constraints) Constraints are small functions that clamp item position and size during drag or resize. They run in order: grid-level `constraints` first, then per-item `constraints` on the `LayoutItem`. ## Built-in constraints | Constraint | Description | |------------|-------------| | `gridBounds` | Keep items inside the grid bounds. | | `minMaxSize` | Respect `minW`, `maxW`, `minH`, `maxH` on items. | | `containerBounds` | Clamp movement to the visible container height. | | `boundedX` | Clamp horizontal movement only. | | `boundedY` | Clamp vertical movement only. | | `aspectRatio(ratio)` | Maintain a width/height ratio while resizing. | | `snapToGrid(stepX, stepY)` | Snap movement to grid steps. | | `minSize(minW, minH)` | Enforce minimum size. | | `maxSize(maxW, maxH)` | Enforce maximum size. | | `defaultConstraints` | `[gridBounds, minMaxSize]`. | ## Using constraints ```tsx import { useState } from "react"; import { DndGrid, type Layout, type LayoutConstraint, aspectRatio, defaultConstraints, snapToGrid, } from "@dnd-grid/react"; const topBand: LayoutConstraint = { name: "topBand(2)", constrainPosition(item, x, y) { const maxY = Math.max(0, 2 - item.h); return { x, y: Math.min(y, maxY) }; }, }; const layout: Layout = [ { id: "snap", x: 0, y: 0, w: 4, h: 2 }, { id: "ratio", x: 4, y: 0, w: 4, h: 2 }, { id: "band", x: 8, y: 0, w: 4, h: 2, constraints: [topBand] }, ]; const constraints = [ ...defaultConstraints, snapToGrid(2, 1), aspectRatio(16 / 9), ]; export function ConstraintGrid() { const [currentLayout, setLayout] = useState(layout); return ( {currentLayout.map((item) => (
{item.id}
))}
); } ``` ## Writing custom constraints A `LayoutConstraint` can implement either `constrainPosition`, `constrainSize`, or both. The `ConstraintContext` provides grid metrics and the full layout. ```tsx import { type LayoutConstraint } from "@dnd-grid/react"; const lockRightColumn: LayoutConstraint = { name: "lockRightColumn", constrainPosition(item, x, y, { cols }) { const maxX = Math.max(0, cols - item.w); return { x: Math.max(x, maxX), y }; }, }; ``` ## Constraint utilities If you are building your own drag interactions, you can apply the same rules manually with the exported helpers. ```tsx import { applyPositionConstraints, applySizeConstraints, defaultConstraints, } from "@dnd-grid/react"; const position = applyPositionConstraints( defaultConstraints, item, nextX, nextY, context, ); const size = applySizeConstraints( defaultConstraints, item, nextW, nextH, handle, context, ); ``` # Grid items (https://dnd-grid.blode.md/concepts/grid-items) ## Basic items Each child of `DndGrid` is a grid item. The child's `key` must match the layout `id`. ```tsx
Content A
Content B
Content C
``` ## Styling items Grid items are absolutely positioned with transforms. Style your content directly. ```tsx

Dashboard Widget

Widget content goes here

``` ```css .my-widget { background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); padding: 16px; height: 100%; overflow: auto; } ``` > [!NOTE] > Use `height: 100%` to fill the allocated space. ## Item behaviour Use per-item fields to lock items, constrain size, or override grid settings. ```tsx const layout: Layout = [ { id: "header", x: 0, y: 0, w: 12, h: 1, static: true }, { id: "content", x: 0, y: 1, w: 8, h: 4, minW: 4, maxW: 10 }, { id: "custom", x: 8, y: 1, w: 4, h: 4, draggable: false, resizable: true, bounded: true, resizeHandles: ["s", "e"], }, ]; ``` Resize handles support edges (`n`, `e`, `s`, `w`) and corners (`ne`, `nw`, `se`, `sw`). ## Custom components Custom components must forward `ref`, `style`, `className`, and event handlers. ```tsx import { forwardRef } from "react"; interface WidgetProps { title: string; children: React.ReactNode; } const Widget = forwardRef( ({ title, children, ...props }, ref) => { return (

{title}

{children}
); } ); ``` ## Drag regions Limit dragging to a handle or exclude specific elements: ```tsx
Drag here
Content
``` # Layout (https://dnd-grid.blode.md/concepts/layout) ## The layout object The layout is the grid state. It is an array of items with position and size. ```ts import { type Layout } from "@dnd-grid/react"; const layout: Layout = [ { id: "a", x: 0, y: 0, w: 4, h: 2 }, { id: "b", x: 4, y: 0, w: 4, h: 2 }, ]; ``` ### Properties | Prop | Type | Description | |------|------|-------------| | `id` | `string` | Required. Unique id, must match the child's `key`. | | `x` | `number` | Required. Column index (0-based). | | `y` | `number` | Required. Row index (0-based). | | `w` | `number` | Required. Width in columns. | | `h` | `number` | Required. Height in rows. | | `minW` | `number` | Minimum width. | | `maxW` | `number` | Maximum width. | | `minH` | `number` | Minimum height. | | `maxH` | `number` | Maximum height. | | `static` | `boolean` | If `true`, the item is locked. | | `draggable` | `boolean` | Override grid drag setting. | | `resizable` | `boolean` | Override grid resize setting. | For the full `LayoutItem` shape (constraints, resize handles, `data`, and more), see the [Layout item API reference](https://dnd-grid.blode.md/api-reference/layout-item). ## Grid system The grid uses a column and row coordinate system. - **Columns (`x`):** Count is set by `cols` (default 12). - **Rows (`y`):** Rows grow downward; height is set by `rowHeight`. - **Width (`w`):** Number of columns an item spans. - **Height (`h`):** Number of rows an item spans. ## Compaction Compaction controls how items pack when space opens up. ```tsx import { verticalCompactor, horizontalCompactor, noCompactor, } from "@dnd-grid/react"; ``` ## Collisions Choose collision behaviour when items overlap. - **Push (default):** The dragged item pushes others out of the way. - **Prevent collision:** The dragged item is blocked by others. - **Allow overlap:** Items can be placed on top of each other. ```tsx import { verticalCompactor, verticalOverlapCompactor } from "@dnd-grid/react"; ``` ## Persistence The layout is JSON-serialisable, so saving it is straightforward. ```tsx const onLayoutChange = (newLayout) => { localStorage.setItem("grid", JSON.stringify(newLayout)); }; ``` # Styling (https://dnd-grid.blode.md/concepts/styling) ## Required styles The `@dnd-grid/react/styles.css` file includes layout and interaction styles. Always import it. ```tsx import "@dnd-grid/react/styles.css"; ``` ## CSS variables `dnd-grid` uses CSS variables for theming. Override them in global CSS or on a grid container. ```css :root { --dnd-grid-radius: 24px; --dnd-grid-placeholder-bg: rgba(0, 0, 0, 0.05); --dnd-grid-handle-bg: #ffffff; } ``` ### Reference | Variable | Default | Description | | :--- | :--- | :--- | | `--dnd-grid-radius` | `24px` | Border radius for grid items and the placeholder. | | `--dnd-grid-placeholder-bg` | `rgba(0,0,0,0.012)` | Background colour of the drop placeholder. | | `--dnd-grid-handle-bg` | `#ffffff` | Colour of the default resize handle. | ## Item styles Grid items use `.dnd-grid-item` for layout. Non-placeholder items also include `.dnd-grid-item-content` so you can target visuals without affecting the placeholder: ```css .dnd-grid-item-content { background: white; border: 1px solid #e2e8f0; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); } .dnd-grid-item.dnd-draggable-dragging { box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); z-index: 10; } ``` ## State classes Use these classes for states and placeholders: - `.dnd-draggable-dragging`: Item being dragged. - `.resizing`: Item being resized. - `.dnd-grid-placeholder`: Placeholder during drag/resize. - `.is-hovered`: Hover state. ## Custom resize handles Replace the handle UI with a custom component: ```tsx } /> ``` ## Reduced motion `DndGrid` respects `prefers-reduced-motion` for spring and WAAPI effects. You can override the behavior with `reducedMotion`: ```tsx ``` Accepted values: - `"system"` (default): use the OS preference. - `"always"`: disable motion. - `"never"`: always allow motion. - `true`/`false`: aliases for `"always"`/`"never"`. To disable CSS transitions as well, set the transition variables in a media query: ```css @media (prefers-reduced-motion: reduce) { .dnd-grid { --dnd-grid-transition-duration: 0ms; } } ``` ## Animation config Use `animationConfig` to tune spring behavior and drag shadow timings: ```tsx ``` Disable springs or shadow animations entirely: ```tsx ``` # Allow overlap (https://dnd-grid.blode.md/examples/allow-overlap)