🚀 Big News:Socket Has Acquired Secure Annex.Learn More
Socket
Book a DemoSign in
Socket

@dnd-grid/react

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@dnd-grid/react

A drag-and-drop (DnD), resizable grid layout for React

latest
Source
npmnpm
Version
1.1.9
Version published
Weekly downloads
725
-5.72%
Maintainers
1
Weekly downloads
 
Created
Source

@dnd-grid/react

A drag-and-drop (DnD), resizable grid layout with responsive breakpoints for React.

Installation

npm install @dnd-grid/react

Usage

import { useState } from "react";
import { DndGrid, type Layout } from "@dnd-grid/react";
import "@dnd-grid/react/styles.css";

const layout: Layout = [
  { id: "a", x: 0, y: 0, w: 2, h: 2 },
  { id: "b", x: 2, y: 0, w: 2, h: 2 },
  { id: "c", x: 4, y: 0, w: 2, h: 2 },
];

function MyGrid() {
  const [currentLayout, setLayout] = useState(layout);

  return (
    <DndGrid
      layout={currentLayout}
      cols={12}
      rowHeight={30}
      onLayoutChange={(newLayout) => setLayout(newLayout)}
    >
      <div key="a">A</div>
      <div key="b">B</div>
      <div key="c">C</div>
    </DndGrid>
  );
}

Headless usage (advanced)

DndGrid is the recommended default. If you need a custom wrapper or want to control item rendering, use the headless useDndGrid hook and render GridItem manually.

Docs: https://dnd-grid.com/docs/hooks/use-dnd-grid

Auto width by default

DndGrid and ResponsiveDndGrid measure container width with ResizeObserver. Use containerProps to style the measurement wrapper, and measureBeforeMount / initialWidth to control the first render.

import { DndGrid, type Layout } from "@dnd-grid/react";

const layout: Layout = [
  { id: "a", x: 0, y: 0, w: 2, h: 2 },
  { id: "b", x: 2, y: 0, w: 2, h: 2 },
];

function MyGrid() {
  return (
    <DndGrid
      layout={layout}
      cols={12}
      rowHeight={30}
      containerProps={{ className: "w-full", style: { maxWidth: 600 } }}
    >
      <div key="a">A</div>
      <div key="b">B</div>
    </DndGrid>
  );
}

Explicit width (when you already have it)

If you already measure width (SSR or custom measurement), pass the width directly:

import {
  DndGrid,
  type Layout,
  useContainerWidth,
} from "@dnd-grid/react";

const layout: Layout = [
  { id: "a", x: 0, y: 0, w: 2, h: 2 },
  { id: "b", x: 2, y: 0, w: 2, h: 2 },
];

function MyGrid() {
  const { width, containerRef, mounted } = useContainerWidth({
    measureBeforeMount: true,
    initialWidth: 600,
  });

  return (
    <div ref={containerRef} className="w-full" style={{ maxWidth: 600 }}>
      {mounted && width > 0 && (
        <DndGrid
          layout={layout}
          cols={12}
          rowHeight={30}
          width={width}
        >
          <div key="a">A</div>
          <div key="b">B</div>
        </DndGrid>
      )}
    </div>
  );
}

Features

  • 100% React - no jQuery
  • Compatible with server-rendered apps
  • Drag-and-drop widgets
  • Resizable widgets
  • Auto-scroll near edges while dragging
  • State-aware styling via data attributes and item hooks
  • Static widgets
  • Configurable packing: horizontal, vertical, or off
  • Bounds checking for dragging and resizing
  • Widgets may be added or removed without rebuilding grid
  • Layout can be serialised and restored
  • Grid items placed using CSS transforms

Props

PropTypeDefaultDescription
childrenReactNoderequiredGrid items; each child key must match a layout id.
layoutLayout[]Array of layout items; missing entries are derived from children (dev warning).
widthnumberundefinedOptional container width in pixels. When provided, measurement is skipped.
measureBeforeMountbooleantrueDelay rendering until the container width is measured.
initialWidthnumber1280Width used before measurement when rendering early.
containerPropsHTMLAttributes<HTMLDivElement>undefinedProps applied to the measurement wrapper.
colsnumber12Number of columns
rowHeightnumber150Height of a row in pixels
autoSizebooleantrueAuto-size the container height
maxRowsnumberInfinityMaximum number of rows
gapnumber | { top: number; right: number; bottom: number; left: number }10Gap around items
containerPaddingnumber | { top: number; right: number; bottom: number; left: number } | nullnullContainer padding (null uses gap)
classNamestring""Extra class for the grid container
styleCSSProperties{}Inline styles for the grid container
draggablebooleantrueEnable dragging
resizablebooleantrueEnable resizing
autoScrollboolean | AutoScrollOptionstrueAuto-scroll when dragging near scroll edges
boundedbooleanfalseKeep items within container bounds
compactorCompactorverticalCompactorCompaction strategy
constraintsLayoutConstraint[]defaultConstraintsConstraints applied during drag/resize
validationbooleantrue in dev, false in prodValidate layouts at runtime with Zod
callbackThrottlenumber | { drag?: number; resize?: number }undefinedThrottle onDrag and onResize callbacks
animationConfigAnimationConfigdefault configConfigure spring and shadow animations
reducedMotionReducedMotionSetting | boolean"system"Motion preference override
resizeHandlesResizeHandleAxis[]["se"]Resize handle positions
resizeHandleReactElement | ((axis, ref) => ReactElement)undefinedCustom resize handle component
transformScalenumber1Scale factor for CSS transforms
dragTouchDelayDurationnumber250Touch delay before drag starts (ms)
dragHandlestring""Selector for drag handles
dragCancelstring""Selector for elements that cancel drag
droppingItemPartial<LayoutItem>{ id: "__dropping-elem__", w: 1, h: 1 }Defaults for the dropping placeholder
slotPropsSlotPropsundefinedSlot styling for items, placeholders, and handles
liveAnnouncementsLiveAnnouncementsOptions | falseenabledConfigure aria-live announcements or disable
innerRefRef<HTMLDivElement>undefinedRef to the container element
aria-labelstringundefinedAccessible label for the grid
aria-labelledbystringundefinedID(s) of labelling elements
aria-describedbystringundefinedID(s) of descriptive elements

Layout is provided via the layout prop; data-grid on children is not supported. Missing layout entries are derived from children (with a dev warning).

Drop behavior is enabled when you provide onDrop or onDropDragOver.

DndGrid renders role="grid" and assigns role="gridcell" plus row/column indices to items. Placeholders and static items are excluded from set indexing.

Responsive layouts

Use ResponsiveDndGrid for breakpoint-aware layouts:

import { ResponsiveDndGrid, type ResponsiveLayouts } from "@dnd-grid/react";

const layouts: ResponsiveLayouts = {
  lg: [{ id: "a", x: 0, y: 0, w: 3, h: 2 }],
  md: [{ id: "a", x: 0, y: 0, w: 4, h: 2 }],
};

function ResponsiveGrid() {
  return (
    <ResponsiveDndGrid
      layouts={layouts}
      gap={{ lg: 16, md: { top: 12, right: 16, bottom: 12, left: 16 } }}
      containerPadding={16}
    >
      <div key="a">A</div>
      <div key="b">B</div>
    </ResponsiveDndGrid>
  );
}

For custom wrappers or manual width control, compose useDndGridResponsiveLayout with DndGrid.

Layout item

Each item in the layout array has these properties:

interface LayoutItem {
  id: string;     // Unique identifier
  x: number;      // X position in grid units
  y: number;      // Y position in grid units
  w: number;      // Width in grid units
  h: number;      // Height in grid units
  minW?: number;  // Minimum width
  maxW?: number;  // Maximum width
  minH?: number;  // Minimum height
  maxH?: number;  // Maximum height
  data?: TData;   // Optional metadata
  resizeHandles?: ResizeHandleAxis[]; // Override resize handle positions
  constraints?: LayoutConstraint[]; // Per-item constraints
  static?: boolean;     // Cannot be moved or resized
  draggable?: boolean; // Override grid draggable
  resizable?: boolean; // Override grid resizable
  bounded?: boolean;   // Override grid bounded
}

Styles

The default stylesheet ships as layout + theme layers. Import the combined file, or split them as needed:

import "@dnd-grid/react/styles.css";
import "@dnd-grid/react/base.css";
import "@dnd-grid/react/theme.css";

Theme variables are exposed as CSS custom properties:

:root {
  --dnd-grid-scale: 1;
  --dnd-grid-radius: 24px;
  --dnd-grid-placeholder-bg: rgba(0, 0, 0, 0.012);
  --dnd-grid-placeholder-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.06);
  --dnd-grid-handle-size: 16px;
  --dnd-grid-handle-bg: rgb(255, 255, 255);
  --dnd-grid-handle-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.06),
    0 9px 9px rgba(0, 0, 0, 0.04),
    0 5.83333px 5.27083px rgba(0, 0, 0, 0.03),
    0 3.46667px 2.86667px rgba(0, 0, 0, 0.024),
    0 1.8px 1.4625px rgba(0, 0, 0, 0.02),
    0 0.733333px 0.733333px rgba(0, 0, 0, 0.016);
  --dnd-grid-transition-duration: 200ms;
  --dnd-grid-transition-easing: cubic-bezier(0.2, 0, 0, 1);
}

Compactors

Compactors control how items are packed after drag/resize. You can use the built-ins or provide your own:

import {
  verticalCompactor,
  horizontalCompactor,
  noCompactor,
  fastVerticalCompactor,
} from "@dnd-grid/react";

<DndGrid compactor={verticalCompactor} />;
<DndGrid compactor={horizontalCompactor} />;
<DndGrid compactor={noCompactor} />;
<DndGrid compactor={fastVerticalCompactor} />;

Constraints

Constraints let you enforce rules during drag/resize:

import { defaultConstraints, aspectRatio, gridBounds } from "@dnd-grid/react";

<DndGrid constraints={[gridBounds, aspectRatio(16 / 9)]} />;

Item state hook

Read the layout item and interaction state from within a child component:

import { useDndGridItemState } from "@dnd-grid/react";

function Card() {
  const { item, state } = useDndGridItemState();
  return (
    <div className={state.resizing ? "is-resizing" : ""}>
      Item {item.id}
    </div>
  );
}

The hook must be used inside a DndGrid item (it throws if rendered elsewhere).

External drag sources

If you manage drag state outside of the grid (for example with @dnd-kit), use the grid handle to drive the dropping placeholder and commit the drop:

const gridRef = useRef<DndGridHandle>(null);

const handleMove = (clientX: number, clientY: number) => {
  gridRef.current?.handleExternalDrag({ clientX, clientY });
};

const handleDrop = (clientX: number, clientY: number, event?: Event) => {
  gridRef.current?.handleExternalDrag({
    clientX,
    clientY,
    event,
    type: "drop",
  });
};

const handleCancel = (clientX: number, clientY: number, event?: Event) => {
  gridRef.current?.handleExternalDrag({
    clientX,
    clientY,
    event,
    type: "cancel",
  });
};

Use onDropDragOver to override the dropping placeholder size while dragging. If your drag library exposes a native event, pass it via event instead of generating one.

Callbacks

CallbackTypeDescription
onLayoutChange(layout: Layout) => voidCalled when layout changes
onDragStart(event: GridDragEvent) => voidCalled when drag starts
onDrag(event: GridDragEvent) => voidCalled during drag
onDragEnd(event: GridDragEvent) => voidCalled when drag ends
onResizeStart(event: GridResizeEvent) => voidCalled when resize starts
onResize(event: GridResizeEvent) => voidCalled during resize
onResizeEnd(event: GridResizeEvent) => voidCalled when resize ends
onDrop(layout, item, e) => voidCalled when item is dropped
onDropDragOver(e) => { w?: number; h?: number } | falseCustomise dropping item

CSS classes

Common CSS classes include:

  • .dnd-grid - Grid container
  • .dnd-grid-item - Grid item
  • .dnd-grid-item-content - Non-placeholder grid item
  • .dnd-grid-placeholder - Placeholder during drag/resize
  • .dnd-draggable - Draggable item
  • .dnd-draggable-dragging - Item being dragged
  • .resizing - Item currently being resized
  • .static - Static (locked) item
  • .dnd-grid-animating - Item settling after drag
  • .dropping - Dropping placeholder item
  • .dnd-grid-resize-handle - Resize handle element
  • body.dnd-grid-dragging - Global dragging state
  • body.dnd-grid-resizing - Global resizing state

Data attributes

The grid sets data-* attributes for stateful styling:

  • data-dnd-grid on the container
  • data-dnd-grid-live-region on the aria-live element
  • data-dnd-grid-item on each item
  • data-dnd-grid-item-id on each item
  • data-dnd-grid-handle and data-handle-axis on resize handles
  • data-dragging, data-resizing, data-settling, data-disabled, data-draggable, data-resizable on items when true
[data-dnd-grid-item][data-resizing] {
  z-index: 10;
}

[data-dnd-grid-handle][data-handle-axis="sw"] {
  cursor: sw-resize;
}

Acknowledgements

This project is based on react-grid-layout by Samuel Reed (STRML). The original project provided the foundation for the grid layout algorithms and core functionality.

Licence

MIT Licence - Copyright (c) 2025 Matthew Blode

Keywords

react

FAQs

Package last updated on 05 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts