);
}
```
## Providing Grid Width
The `width` prop is required. You have several options:
### Option 1: useContainerWidth Hook (Recommended)
```tsx
import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
function MyGrid() {
const { width, containerRef, mounted } = useContainerWidth();
return (
{mounted && ...}
);
}
```
### Option 2: Fixed Width
```tsx
...
```
### Option 3: CSS Container Queries or ResizeObserver
Use any width measurement library like [react-sizeme](https://github.com/ctrlplusb/react-sizeme) or your own ResizeObserver implementation.
### Option 4: Legacy WidthProvider HOC
For backwards compatibility, you can still use `WidthProvider`:
```tsx
import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
function MyGrid() {
return ...;
}
```
## Hooks API
The v2 API provides three hooks for different use cases. Choose based on your needs:
| Hook | Use When |
| --------------------- | -------------------------------------------------------------------- |
| `useContainerWidth` | You need responsive width measurement (most common) |
| `useGridLayout` | You're building a custom grid component or need direct state control |
| `useResponsiveLayout` | You're building a custom responsive grid with breakpoint logic |
### useContainerWidth
Observes container width using ResizeObserver and provides reactive width updates. This is the recommended way to provide width to the grid.
**Why use it instead of WidthProvider?**
- Hooks are more composable and easier to test
- No HOC wrapper means simpler component tree
- Explicit control over when to render (via `mounted`)
- Works better with SSR
```tsx
import { useContainerWidth } from "react-grid-layout";
function MyGrid() {
const { width, containerRef, mounted, measureWidth } = useContainerWidth({
measureBeforeMount: false, // Set true for SSR
initialWidth: 1280 // Width before first measurement
});
return (
{mounted && }
);
}
```
**Type Definitions:**
```ts
interface UseContainerWidthOptions {
/** Delay render until width is measured. Useful for SSR. Default: false */
measureBeforeMount?: boolean;
/** Initial width before measurement. Default: 1280 */
initialWidth?: number;
}
interface UseContainerWidthResult {
/** Current container width in pixels */
width: number;
/** Whether the container has been measured at least once */
mounted: boolean;
/** Ref to attach to the container element */
containerRef: RefObject;
/** Manually trigger a width measurement */
measureWidth: () => void;
}
```
### useGridLayout
Core layout state management hook. Use this when you need direct control over drag/resize/drop state, or when building a custom grid component.
**Why use it instead of the component?**
- Full control over layout state and updates
- Access to drag/resize/drop state for custom UIs
- Can integrate with external state management
- Build headless grid implementations
```tsx
import { useGridLayout, horizontalCompactor } from "react-grid-layout";
function CustomGrid({ initialLayout }) {
const {
layout,
setLayout,
dragState,
resizeState,
onDragStart,
onDrag,
onDragStop,
onResizeStart,
onResize,
onResizeStop,
containerHeight,
isInteracting,
compactor
} = useGridLayout({
layout: initialLayout,
cols: 12,
compactor: horizontalCompactor, // default is verticalCompactor
onLayoutChange: newLayout => console.log("Layout changed:", newLayout)
});
// Access drag state for custom placeholder rendering
const placeholder = dragState.activeDrag;
// Check if any interaction is happening
if (isInteracting) {
// Disable other UI during drag/resize
}
return (
{layout.map(item => (
onDragStart(item.i, item.x, item.y)}
>
{item.i}
))}
{placeholder && }
);
}
```
**Type Definitions:**
```ts
interface UseGridLayoutOptions {
/** Initial layout */
layout: Layout;
/** Number of columns */
cols: number;
/** Block movement into occupied space instead of pushing items */
preventCollision?: boolean;
/** Called when layout changes */
onLayoutChange?: (layout: Layout) => void;
/** Compactor for layout compaction (default: verticalCompactor) */
compactor?: Compactor;
}
interface UseGridLayoutResult {
/** Current layout */
layout: Layout;
/** Set layout directly */
setLayout: (layout: Layout) => void;
/** Current drag state (activeDrag, oldDragItem, oldLayout) */
dragState: DragState;
/** Current resize state (resizing, oldResizeItem, oldLayout) */
resizeState: ResizeState;
/** Current drop state (droppingDOMNode, droppingPosition) */
dropState: DropState;
/** Start dragging an item */
onDragStart: (itemId: string, x: number, y: number) => LayoutItem | null;
/** Update drag position */
onDrag: (itemId: string, x: number, y: number) => void;
/** Stop dragging */
onDragStop: (itemId: string, x: number, y: number) => void;
/** Start resizing an item */
onResizeStart: (itemId: string) => LayoutItem | null;
/** Update resize dimensions */
onResize: (
itemId: string,
w: number,
h: number,
x?: number,
y?: number
) => void;
/** Stop resizing */
onResizeStop: (itemId: string, w: number, h: number) => void;
/** Handle external drag over */
onDropDragOver: (
droppingItem: LayoutItem,
position: DroppingPosition
) => void;
/** Handle external drag leave */
onDropDragLeave: () => void;
/** Complete external drop */
onDrop: (droppingItem: LayoutItem) => void;
/** Container height in grid rows */
containerHeight: number;
/** Whether any drag/resize/drop is active */
isInteracting: boolean;
/** The compactor being used */
compactor: Compactor;
}
```
### useResponsiveLayout
Manages responsive breakpoints and generates layouts for different screen sizes. Use this when building a custom responsive grid.
**Why use it instead of the Responsive component?**
- Direct access to current breakpoint
- Control over layout generation for new breakpoints
- Can update layouts for specific breakpoints
- Build custom breakpoint UIs
```tsx
import { useContainerWidth, useResponsiveLayout } from "react-grid-layout";
function CustomResponsiveGrid() {
const { width, containerRef, mounted } = useContainerWidth();
const {
layout, // Current layout for active breakpoint
layouts, // All layouts by breakpoint
breakpoint, // Current active breakpoint ('lg', 'md', etc.)
cols, // Column count for current breakpoint
setLayoutForBreakpoint,
setLayouts,
sortedBreakpoints
} = useResponsiveLayout({
width,
breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 },
cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
layouts: {
lg: [{ i: "1", x: 0, y: 0, w: 2, h: 2 }],
md: [{ i: "1", x: 0, y: 0, w: 3, h: 2 }]
},
// compactor: verticalCompactor (default)
onBreakpointChange: (bp, cols) =>
console.log(`Now at ${bp} (${cols} cols)`),
onLayoutChange: (layout, allLayouts) => saveToServer(allLayouts)
});
// Show current breakpoint in UI
return (
```
### Compactor
Layout compaction strategy. Built-in options:
```ts
import {
verticalCompactor, // Default: compact items upward
horizontalCompactor, // Compact items leftward
noCompactor, // No compaction (free positioning)
getCompactor // Factory: getCompactor('vertical', allowOverlap, preventCollision)
} from "react-grid-layout/core";
```
### ResponsiveGridLayout Props
Extends `GridLayoutProps` with responsive-specific props:
```ts
interface ResponsiveGridLayoutProps {
// Responsive configuration
breakpoint?: B; // Current breakpoint (auto-detected)
breakpoints?: Record; // Breakpoint definitions (default: {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0})
cols?: Record; // Columns per breakpoint (default: {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2})
layouts?: Record; // Layouts per breakpoint
// Can be fixed or per-breakpoint
margin?: [number, number] | Partial>;
containerPadding?:
| [number, number]
| Partial>
| null;
// Callbacks
onBreakpointChange?: (newBreakpoint: B, cols: number) => void;
onLayoutChange?: (layout: Layout, layouts: Record) => void;
onWidthChange?: (
width: number,
margin: [number, number],
cols: number,
padding: [number, number] | null
) => void;
}
```
### Layout Item
```ts
interface LayoutItem {
i: string; // Unique identifier (must match child key)
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 (default: 0)
maxW?: number; // Maximum width (default: Infinity)
minH?: number; // Minimum height (default: 0)
maxH?: number; // Maximum height (default: Infinity)
static?: boolean; // If true, not draggable or resizable
isDraggable?: boolean; // Override grid isDraggable
isResizable?: boolean; // Override grid isResizable
isBounded?: boolean; // Override grid isBounded
resizeHandles?: Array<"s" | "w" | "e" | "n" | "sw" | "nw" | "se" | "ne">;
}
```
### Core Utilities
Import pure layout functions from `react-grid-layout/core`:
```ts
import {
verticalCompactor,
horizontalCompactor,
noCompactor,
getCompactor,
moveElement,
collides,
getFirstCollision,
validateLayout
// ... and more
} from "react-grid-layout/core";
```
> **Note**: The `compact()` function is not exported. Use compactors instead: `verticalCompactor.compact(layout, cols)` or get one via `getCompactor('vertical')`.
## Extending: Custom Compactors & Position Strategies
### Creating a Custom Compactor
Compactors control how items are arranged after drag/resize. Create your own for custom layouts like masonry, gravity, or shelf-packing.
**The Compactor Interface:**
```ts
interface Compactor {
/** Identifies the compaction type */
type: "vertical" | "horizontal" | null | string;
/**
* Whether items can overlap each other.
*
* When true:
* - Items can be placed on top of other items
* - Dragging into another item does NOT push it away
* - Compaction is skipped after drag/resize
*
* Use for: layered dashboards, free-form layouts
*/
allowOverlap: boolean;
/**
* Whether to block movement that would cause collision.
*
* When true (and allowOverlap is false):
* - Dragging into another item is blocked (item snaps back)
* - Other items are NOT pushed away
* - Only affects movement, not compaction
*
* Use with noCompactor for: fixed grids, slot-based layouts
*
* Note: Has no effect when allowOverlap is true.
*/
preventCollision?: boolean;
/**
* Compact the entire layout.
* Called after any layout change to fill gaps.
*
* @param layout - Array of layout items (clone before mutating!)
* @param cols - Number of grid columns
* @returns New compacted layout
*/
compact(layout: Layout, cols: number): Layout;
}
```
**Example: Gravity Compactor (items fall to bottom)**
```ts
import { cloneLayout, cloneLayoutItem, getStatics, bottom } from "react-grid-layout/core";
const gravityCompactor: Compactor = {
type: "gravity",
allowOverlap: false,
compact(layout, cols) {
const statics = getStatics(layout);
const maxY = 100; // arbitrary max height
const out = [];
// Sort by Y descending (process bottom items first)
const sorted = [...layout].sort((a, b) => b.y - a.y);
for (const item of sorted) {
const l = cloneLayoutItem(item);
if (!l.static) {
// Move down as far as possible
while (l.y < maxY && !collides(l, statics)) {
l.y++;
}
l.y--; // Back up one
}
out.push(l);
}
return out;
}
};
// Usage
```
**Example: Single Row Compactor (horizontal shelf)**
```ts
const singleRowCompactor: Compactor = {
type: "shelf",
allowOverlap: false,
compact(layout, cols) {
let x = 0;
const out = [];
// Sort by original X position
const sorted = [...layout].sort((a, b) => a.x - b.x);
for (const item of sorted) {
const l = cloneLayoutItem(item);
if (!l.static) {
l.x = x;
l.y = 0; // All items on row 0
x += l.w;
// Wrap to next row if overflow
if (x > cols) {
l.x = 0;
x = l.w;
}
}
out.push(l);
}
return out;
}
};
```
**Using Helper Functions:**
The core module exports helpers for building compactors:
```ts
import {
resolveCompactionCollision, // Move items to resolve overlaps
compactItemVertical, // Compact one item upward
compactItemHorizontal, // Compact one item leftward
getFirstCollision, // Find first collision
collides, // Check if two items collide
getStatics, // Get static items from layout
cloneLayout, // Clone layout array
cloneLayoutItem // Clone single item
} from "react-grid-layout/core";
```
### Creating a Custom Position Strategy
Position strategies control how items are positioned via CSS. Create custom strategies for special transform handling.
**The PositionStrategy Interface:**
```ts
interface PositionStrategy {
/** Type identifier */
type: "transform" | "absolute" | string;
/** Scale factor for coordinate calculations */
scale: number;
/**
* Generate CSS styles for positioning an item.
*
* @param pos - Position with top, left, width, height in pixels
* @returns CSS properties object
*/
calcStyle(pos: Position): React.CSSProperties;
/**
* Calculate drag position from mouse coordinates.
* Used during drag to convert screen coords to grid coords.
*
* @param clientX - Mouse X position
* @param clientY - Mouse Y position
* @param offsetX - Offset from item left edge
* @param offsetY - Offset from item top edge
* @returns Calculated left/top position
*/
calcDragPosition(
clientX: number,
clientY: number,
offsetX: number,
offsetY: number
): { left: number; top: number };
}
```
**Example: Rotated Container Strategy**
```ts
const createRotatedStrategy = (angleDegrees: number): PositionStrategy => {
const angleRad = (angleDegrees * Math.PI) / 180;
const cos = Math.cos(angleRad);
const sin = Math.sin(angleRad);
return {
type: "rotated",
scale: 1,
calcStyle(pos) {
// Apply rotation to position
const rotatedX = pos.left * cos - pos.top * sin;
const rotatedY = pos.left * sin + pos.top * cos;
return {
transform: `translate(${rotatedX}px, ${rotatedY}px)`,
width: `${pos.width}px`,
height: `${pos.height}px`,
position: "absolute"
};
},
calcDragPosition(clientX, clientY, offsetX, offsetY) {
// Reverse the rotation for drag calculations
const x = clientX - offsetX;
const y = clientY - offsetY;
return {
left: x * cos + y * sin,
top: -x * sin + y * cos
};
}
};
};
// Usage: grid inside a rotated container
```
**Example: 3D Perspective Strategy**
```ts
const create3DStrategy = (
perspective: number,
rotateX: number
): PositionStrategy => ({
type: "3d",
scale: 1,
calcStyle(pos) {
return {
transform: `
perspective(${perspective}px)
rotateX(${rotateX}deg)
translate3d(${pos.left}px, ${pos.top}px, 0)
`,
width: `${pos.width}px`,
height: `${pos.height}px`,
position: "absolute",
transformStyle: "preserve-3d"
};
},
calcDragPosition(clientX, clientY, offsetX, offsetY) {
// Adjust for perspective foreshortening
const perspectiveFactor = 1 + clientY / perspective;
return {
left: (clientX - offsetX) / perspectiveFactor,
top: (clientY - offsetY) / perspectiveFactor
};
}
});
```
## Extras
The `react-grid-layout/extras` entry point provides optional components that extend react-grid-layout. These are tree-shakeable and won't be included in your bundle unless explicitly imported.
### GridBackground
Renders an SVG grid background that aligns with GridLayout cells. Use this to visualize the grid structure behind your layout.
> Based on [PR #2175](https://github.com/react-grid-layout/react-grid-layout/pull/2175) by [@dmj900501](https://github.com/dmj900501).
```tsx
import { GridBackground } from "react-grid-layout/extras";
import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
function MyGrid() {
const { width, containerRef, mounted } = useContainerWidth();
return (
{mounted && (
<>
{children}
>
)}
);
}
```
**Props:**
```ts
interface GridBackgroundProps {
// Required - must match your GridLayout config
width: number; // Container width
cols: number; // Number of columns
rowHeight: number; // Row height in pixels
// Optional
margin?: [number, number]; // Gap between cells (default: [10, 10])
containerPadding?: [number, number] | null; // Container padding (default: uses margin)
rows?: number | "auto"; // Number of rows to display (default: 10)
height?: number; // Used when rows="auto" to calculate row count
color?: string; // Cell background color (default: "#e0e0e0")
borderRadius?: number; // Cell border radius (default: 4)
className?: string; // Additional CSS class
style?: React.CSSProperties; // Additional inline styles
}
```
### Fast Compactors
For large layouts (200+ items), the standard compactors can become slow due to O(n²) collision resolution. The fast compactors use optimized algorithms with O(n log n) complexity.
> Based on the "rising tide" algorithm from [PR #2152](https://github.com/react-grid-layout/react-grid-layout/pull/2152) by [@morris](https://github.com/morris).
```tsx
import {
fastVerticalCompactor,
fastHorizontalCompactor,
fastVerticalOverlapCompactor,
fastHorizontalOverlapCompactor
} from "react-grid-layout/extras";
;
```
**Performance Benchmarks:**
| Items | Standard Vertical | Fast Vertical | Speedup |
| ----- | ----------------- | ------------- | ------- |
| 50 | 112 µs | 19 µs | **6x** |
| 100 | 203 µs | 36 µs | **6x** |
| 200 | 821 µs | 51 µs | **16x** |
| 500 | 5.7 ms | 129 µs | **45x** |
| Items | Standard Horizontal | Fast Horizontal | Speedup |
| ----- | ------------------- | --------------- | ------- |
| 50 | 164 µs | 12 µs | **14x** |
| 100 | 477 µs | 25 µs | **19x** |
| 200 | 1.1 ms | 42 µs | **26x** |
| 500 | 9.5 ms | 128 µs | **74x** |
**Correctness:**
The fast compactors produce layouts identical to the standard compactors:
- **Vertical**: 0% height difference on deterministic 100-item layouts
- **Horizontal**: 0% width difference on deterministic 100-item layouts
- Both pass all correctness tests: no overlaps, idempotent, static item handling
**When to use:**
- Use fast compactors for dashboards with 200+ widgets
- For smaller layouts (<100 items), standard compactors work equally well
- Both standard and fast compactors produce valid, non-overlapping layouts
### calcGridCellDimensions (Core Utility)
For building custom grid overlays or backgrounds, use the `calcGridCellDimensions` utility from `react-grid-layout/core`:
```ts
import { calcGridCellDimensions } from "react-grid-layout/core";
const dims = calcGridCellDimensions({
width: 1200,
cols: 12,
rowHeight: 30,
margin: [10, 10],
containerPadding: [20, 20]
});
// dims = {
// cellWidth: 88.33, // Width of each cell
// cellHeight: 30, // Height of each cell (= rowHeight)
// offsetX: 20, // Left padding
// offsetY: 20, // Top padding
// gapX: 10, // Horizontal gap between cells
// gapY: 10, // Vertical gap between cells
// cols: 12, // Column count
// containerWidth: 1200
// }
```
This is useful for building custom visualizations, snap-to-grid functionality, or integrating with canvas/WebGL renderers.
## Performance
### Memoize Children
The grid compares children by reference. Memoize them for better performance:
```tsx
function MyGrid({ count, width }) {
const children = useMemo(() => {
return Array.from({ length: count }, (_, i) => (
));
}, [count]);
return (
{children}
);
}
```
### Avoid Creating Components in Render (Legacy WidthProvider)
If using the legacy WidthProvider HOC, don't create the component during render:
```tsx
import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";
// Bad - creates new component every render
function MyGrid() {
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
return ...;
}
// Good - create once outside or with useMemo
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
function MyGrid() {
return ...;
}
```
With the v2 API, use `useContainerWidth` hook instead to avoid this issue entirely.
## Custom Child Components
Grid children must forward refs and certain props:
```tsx
const CustomItem = forwardRef(
(
{
style,
className,
onMouseDown,
onMouseUp,
onTouchEnd,
children,
...props
},
ref
) => {
return (
{children}
);
}
);
```
## Contribute
If you have a feature request, please add it as an issue or make a pull request.
If you have a bug to report, please reproduce the bug in [CodeSandbox](https://codesandbox.io/p/sandbox/5ywf7c) to help
us easily isolate it.