Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

918 Commits

Repository files navigation

React-Grid-Layout

npm packagenpm downloads

React-Grid-Layout is a grid layout system much like Packery or Gridster, for React.

Unlike those systems, it is responsive and supports breakpoints. Breakpoint layouts can be provided by the user or autogenerated.

RGL is React-only and does not require jQuery.

BitMEX UI

GIF from production usage on BitMEX.com

[Demo | Changelog | CodeSandbox Editable demo]

Table of Contents

What's New in v2

Version 2 is a complete TypeScript rewrite with a modernized API:

  • Full TypeScript support - First-class types, no more @types/react-grid-layout
  • React Hooks - New useContainerWidth, useGridLayout, and useResponsiveLayout hooks
  • Composable Configuration - Group related props into focused interfaces:
    • gridConfig - cols, rowHeight, margin, padding
    • dragConfig - enable, handle, cancel, bounded
    • resizeConfig - enable, handles
    • positionStrategy - transform vs absolute positioning
    • compactor - vertical, horizontal, or custom algorithms
  • Modular architecture - Import only what you need:
    • react-grid-layout - React components and hooks (v2 API)
    • react-grid-layout/core - Pure layout algorithms (framework-agnostic)
    • react-grid-layout/legacy - v1 flat props API for migration
    • react-grid-layout/extras - Optional components like GridBackground
  • Smaller bundle - Tree-shakeable ESM and CJS builds

Breaking Changes

See the RFC for detailed migration examples.

ChangeDescription
width prop requiredUse useContainerWidth hook or provide your own measurement
onDragStart thresholdNow fires after 3px movement, not on mousedown. Use onMouseDown for immediate response
Immutable callbacksCallback parameters are read-only. Use onLayoutChange or constraints instead of mutation
data-grid in legacy onlyv2 requires explicit layout prop. Use legacy wrapper for data-grid
Pluggable compactionCompaction is now pluggable via Compactor interface. Optional fast O(n log n) algorithm in /extras
UMD bundle removedUse a bundler (Vite, webpack, esbuild)
verticalCompact removedUse compactType={null} or compactor={noCompactor}

Migrating from v1

Quick migration - change your import to use the legacy wrapper:

- import GridLayout, { Responsive, WidthProvider } from 'react-grid-layout';+ import GridLayout, { Responsive, WidthProvider } from 'react-grid-layout/legacy';

This provides 100% runtime API compatibility with v1.

TypeScript users: If you were using @types/react-grid-layout, note that v2 includes its own types with some naming changes:

Old (@types/react-grid-layout)New (v2)Notes
RGL.LayoutLayoutItemSingle grid item
RGL.Layout[]LayoutArray of items
RGL.LayoutsResponsiveLayoutsBreakpoint → layout map
- import RGL from 'react-grid-layout';- const item: RGL.Layout = { i: 'a', x: 0, y: 0, w: 1, h: 1 };- const layouts: RGL.Layouts = { lg: [item] };+ import { LayoutItem, ResponsiveLayouts } from 'react-grid-layout/legacy';+ const item: LayoutItem = { i: 'a', x: 0, y: 0, w: 1, h: 1 };+ const layouts: ResponsiveLayouts = { lg: [item] };

Full migration - adopt the v2 API for new features and better tree-shaking:

importReactGridLayout,{useContainerWidth,verticalCompactor}from'react-grid-layout';functionMyGrid(){const{ width, containerRef, mounted }=useContainerWidth();return(<divref={containerRef}>{mounted&&(<ReactGridLayoutwidth={width}layout={layout}gridConfig={{cols: 12,rowHeight: 30}}dragConfig={{enabled: true,handle: '.handle'}}compactor={verticalCompactor}>{children}</ReactGridLayout>)}</div>);}
Use CaseRecommendation
Existing v1 codebasereact-grid-layout/legacy
New projectv2 API with hooks
Custom compactionv2 with custom Compactor
SSRv2 with measureBeforeMount: true

Demos

  1. Showcase
  2. Basic
  3. No Dragging/Resizing (Layout Only)
  4. Messy Layout Autocorrect
  5. Layout Defined on Children
  6. Static Elements
  7. Adding/Removing Elements
  8. Saving Layout to LocalStorage
  9. Saving a Responsive Layout to LocalStorage
  10. Minimum and Maximum Width/Height
  11. Dynamic Minimum and Maximum Width/Height
  12. Toolbox
  13. Drag From Outside
  14. Bounded Layout
  15. Responsive Bootstrap-style Layout
  16. Scaled Containers
  17. Allow Overlap
  18. All Resizable Handles
  19. Compactor Showcase
  20. Pluggable Constraints
  21. Aspect Ratio Constraints
  22. Custom Constraints

Projects Using React-Grid-Layout

Know of others? Create a PR to let me know!

Features

  • 100% React - no jQuery
  • Full TypeScript support
  • Compatible with server-rendered apps
  • Draggable widgets
  • Resizable widgets
  • 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 serialized and restored
  • Responsive breakpoints
  • Separate layouts per responsive breakpoint
  • Grid Items placed using CSS Transforms
  • Compatibility with <React.StrictMode>
VersionCompatibility
>= 2.0.0React 18+, TypeScript
>= 0.17.0React 16 & 17

Installation

npm install react-grid-layout

Include the stylesheets in your application:

import"react-grid-layout/css/styles.css";import"react-resizable/css/styles.css";

Or link them directly:

<linkrel="stylesheet" href="/node_modules/react-grid-layout/css/styles.css" /><linkrel="stylesheet" href="/node_modules/react-resizable/css/styles.css" />

Quick Start

importReactGridLayout,{useContainerWidth}from"react-grid-layout";import"react-grid-layout/css/styles.css";import"react-resizable/css/styles.css";functionMyGrid(){const{ width, containerRef, mounted }=useContainerWidth();constlayout=[{i: "a",x: 0,y: 0,w: 1,h: 2,static: true},{i: "b",x: 1,y: 0,w: 3,h: 2,minW: 2,maxW: 4},{i: "c",x: 4,y: 0,w: 1,h: 2}];return(<divref={containerRef}>{mounted&&(<ReactGridLayoutlayout={layout}width={width}gridConfig={{cols: 12,rowHeight: 30}}><divkey="a">a</div><divkey="b">b</div><divkey="c">c</div></ReactGridLayout>)}</div>);}

You can also define layout on children using data-grid:

<ReactGridLayoutwidth={width}gridConfig={{cols: 12,rowHeight: 30}}><divkey="a"data-grid={{x: 0,y: 0,w: 1,h: 2,static: true}}>
a
</div><divkey="b"data-grid={{x: 1,y: 0,w: 3,h: 2}}>
b
</div><divkey="c"data-grid={{x: 4,y: 0,w: 1,h: 2}}>
c
</div></ReactGridLayout>

Responsive Usage

Use Responsive for automatic breakpoint handling:

import{Responsive,useContainerWidth}from"react-grid-layout";functionMyResponsiveGrid(){const{ width, containerRef, mounted }=useContainerWidth();constlayouts={lg: [{i: "1",x: 0,y: 0,w: 2,h: 2}],md: [{i: "1",x: 0,y: 0,w: 2,h: 2}]};return(<divref={containerRef}>{mounted&&(<Responsivelayouts={layouts}breakpoints={{lg: 1200,md: 996,sm: 768,xs: 480,xxs: 0}}cols={{lg: 12,md: 10,sm: 6,xs: 4,xxs: 2}}width={width}><divkey="1">1</div><divkey="2">2</div><divkey="3">3</div></Responsive>)}</div>);}

Providing Grid Width

The width prop is required. You have several options:

Option 1: useContainerWidth Hook (Recommended)

importReactGridLayout,{useContainerWidth}from"react-grid-layout";functionMyGrid(){const{ width, containerRef, mounted }=useContainerWidth();return(<divref={containerRef}>{mounted&&<ReactGridLayoutwidth={width}>...</ReactGridLayout>}</div>);}

Option 2: Fixed Width

<ReactGridLayoutwidth={1200}>...</ReactGridLayout>

Option 3: CSS Container Queries or ResizeObserver

Use any width measurement library like react-sizeme or your own ResizeObserver implementation.

Option 4: Legacy WidthProvider HOC

For backwards compatibility, you can still use WidthProvider:

importReactGridLayout,{WidthProvider}from"react-grid-layout/legacy";constGridLayoutWithWidth=WidthProvider(ReactGridLayout);functionMyGrid(){return<GridLayoutWithWidth>...</GridLayoutWithWidth>;}

Hooks API

The v2 API provides three hooks for different use cases. Choose based on your needs:

HookUse When
useContainerWidthYou need responsive width measurement (most common)
useGridLayoutYou're building a custom grid component or need direct state control
useResponsiveLayoutYou'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
import{useContainerWidth}from"react-grid-layout";functionMyGrid(){const{ width, containerRef, mounted, measureWidth }=useContainerWidth({measureBeforeMount: false,// Set true for SSRinitialWidth: 1280// Width before first measurement});return(<divref={containerRef}>{mounted&&<ReactGridLayoutwidth={width}/>}</div>);}

Type Definitions:

interfaceUseContainerWidthOptions{/** Delay render until width is measured. Useful for SSR. Default: false */measureBeforeMount?: boolean;/** Initial width before measurement. Default: 1280 */initialWidth?: number;}interfaceUseContainerWidthResult{/** 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<HTMLDivElement|null>;/** 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
import{useGridLayout,horizontalCompactor}from"react-grid-layout";functionCustomGrid({ initialLayout }){const{
layout,
setLayout,
dragState,
resizeState,
onDragStart,
onDrag,
onDragStop,
onResizeStart,
onResize,
onResizeStop,
containerHeight,
isInteracting,
compactor
}=useGridLayout({layout: initialLayout,cols: 12,compactor: horizontalCompactor,// default is verticalCompactoronLayoutChange: newLayout=>console.log("Layout changed:",newLayout)});// Access drag state for custom placeholder renderingconstplaceholder=dragState.activeDrag;// Check if any interaction is happeningif(isInteracting){// Disable other UI during drag/resize}return(<divstyle={{height: containerHeight*rowHeight}}>{layout.map(item=>(<divkey={item.i}onMouseDown={()=>onDragStart(item.i,item.x,item.y)}>{item.i}</div>))}{placeholder&&<divclassName="placeholder"/>}</div>);}

Type Definitions:

interfaceUseGridLayoutOptions{/** 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;}interfaceUseGridLayoutResult{/** 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
import{useContainerWidth,useResponsiveLayout}from"react-grid-layout";functionCustomResponsiveGrid(){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 UIreturn(<divref={containerRef}><div>
Current breakpoint: {breakpoint} ({cols} columns)
</div>{mounted&&(<GridLayoutwidth={width}cols={cols}layout={layout}>{/* children */}</GridLayout>)}</div>);}

Type Definitions:

interfaceUseResponsiveLayoutOptions<Bextendsstring=DefaultBreakpoints>{/** Current container width */width: number;/** Breakpoint definitions (name → min-width). Default: {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0} */breakpoints?: Record<B,number>;/** Column counts per breakpoint. Default: {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2} */cols?: Record<B,number>;/** Layouts for each breakpoint */layouts?: Partial<Record<B,Layout>>;/** Compactor for layout compaction (default: verticalCompactor) */compactor?: Compactor;/** Called when breakpoint changes */onBreakpointChange?: (newBreakpoint: B,cols: number)=>void;/** Called when layout changes */onLayoutChange?: (layout: Layout,layouts: Record<B,Layout>)=>void;/** Called when width changes */onWidthChange?: (width: number,margin: [number,number],cols: number,padding: [number,number]|null)=>void;}interfaceUseResponsiveLayoutResult<Bextendsstring=DefaultBreakpoints>{/** Current layout for the active breakpoint */layout: Layout;/** All layouts by breakpoint */layouts: Partial<Record<B,Layout>>;/** Current active breakpoint */breakpoint: B;/** Column count for the current breakpoint */cols: number;/** Update layout for a specific breakpoint */setLayoutForBreakpoint: (breakpoint: B,layout: Layout)=>void;/** Update all layouts */setLayouts: (layouts: Partial<Record<B,Layout>>)=>void;/** Sorted array of breakpoint names (smallest to largest) */sortedBreakpoints: B[];}typeDefaultBreakpoints="lg"|"md"|"sm"|"xs"|"xxs";

API Reference

ReactGridLayout Props

The v2 API uses composable configuration interfaces for cleaner prop organization:

interfaceReactGridLayoutProps{// Requiredchildren: React.ReactNode;width: number;// Container width in pixels// Configuration interfaces (see below for details)gridConfig?: Partial<GridConfig>;// Grid measurement settingsdragConfig?: Partial<DragConfig>;// Drag behavior settingsresizeConfig?: Partial<ResizeConfig>;// Resize behavior settingsdropConfig?: Partial<DropConfig>;// External drop settingspositionStrategy?: PositionStrategy;// CSS positioning strategycompactor?: Compactor;// Layout compaction strategy// Layout datalayout?: Layout;// Layout definitiondroppingItem?: LayoutItem;// Item configuration when dropping from outside// ContainerautoSize?: boolean;// Auto-size container height (default: true)className?: string;style?: React.CSSProperties;innerRef?: React.Ref<HTMLDivElement>;// CallbacksonLayoutChange?: (layout: Layout)=>void;onDragStart?: EventCallback;onDrag?: EventCallback;onDragStop?: EventCallback;onResizeStart?: EventCallback;onResize?: EventCallback;onResizeStop?: EventCallback;onDrop?: (layout: Layout,item: LayoutItem|undefined,e: Event)=>void;onDropDragOver?: (e: DragEvent)=>{w?: number;h?: number}|false|void;}

GridConfig

Grid measurement configuration:

interfaceGridConfig{cols: number;// Number of columns (default: 12)rowHeight: number;// Row height in pixels (default: 150)margin: [number,number];// [x, y] margin between items (default: [10, 10])containerPadding: [number,number]|null;// Container padding (default: null, uses margin)maxRows: number;// Maximum rows (default: Infinity)}

DragConfig

Drag behavior configuration:

interfaceDragConfig{enabled: boolean;// Enable dragging (default: true)bounded: boolean;// Keep items within container (default: false)handle?: string;// CSS selector for drag handlecancel?: string;// CSS selector to cancel draggingthreshold: number;// Pixels to move before drag starts (default: 3)}

ResizeConfig

Resize behavior configuration:

interfaceResizeConfig{enabled: boolean;// Enable resizing (default: true)handles: ResizeHandleAxis[];// Handle positions (default: ['se'])handleComponent?: React.ReactNode|((axis,ref)=>React.ReactNode);}

DropConfig

External drop configuration:

interfaceDropConfig{enabled: boolean;// Allow external drops (default: false)defaultItem: {w: number;h: number};// Default size (default: { w: 1, h: 1 })onDragOver?: (e: DragEvent)=>{w?: number;h?: number}|false|void;}

PositionStrategy

CSS positioning strategy. Built-in options:

import{transformStrategy,// Default: use CSS transformsabsoluteStrategy,// Use top/left positioningcreateScaledStrategy// For scaled containers}from"react-grid-layout/core";// Example: scaled container<divstyle={{transform: 'scale(0.5)'}}><ReactGridLayoutpositionStrategy={createScaledStrategy(0.5)} ... /></div>

Compactor

Layout compaction strategy. Built-in options:

import{verticalCompactor,// Default: compact items upwardhorizontalCompactor,// Compact items leftwardnoCompactor,// No compaction (free positioning)getCompactor// Factory: getCompactor('vertical', allowOverlap, preventCollision)}from"react-grid-layout/core";

ResponsiveGridLayout Props

Extends GridLayoutProps with responsive-specific props:

interfaceResponsiveGridLayoutProps<Bextendsstring=string>{// Responsive configurationbreakpoint?: B;// Current breakpoint (auto-detected)breakpoints?: Record<B,number>;// Breakpoint definitions (default: {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0})cols?: Record<B,number>;// Columns per breakpoint (default: {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2})layouts?: Record<B,Layout>;// Layouts per breakpoint// Can be fixed or per-breakpointmargin?: [number,number]|Partial<Record<B,[number,number]>>;containerPadding?:
|[number,number]|Partial<Record<B,[number,number]|null>>|null;// CallbacksonBreakpointChange?: (newBreakpoint: B,cols: number)=>void;onLayoutChange?: (layout: Layout,layouts: Record<B,Layout>)=>void;onWidthChange?: (width: number,margin: [number,number],cols: number,padding: [number,number]|null)=>void;}

Layout Item

interfaceLayoutItem{i: string;// Unique identifier (must match child key)x: number;// X position in grid unitsy: number;// Y position in grid unitsw: number;// Width in grid unitsh: number;// Height in grid unitsminW?: 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 resizableisDraggable?: boolean;// Override grid isDraggableisResizable?: boolean;// Override grid isResizableisBounded?: boolean;// Override grid isBoundedresizeHandles?: Array<"s"|"w"|"e"|"n"|"sw"|"nw"|"se"|"ne">;}

Core Utilities

Import pure layout functions from react-grid-layout/core:

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:

interfaceCompactor{/** 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)

import{cloneLayout,cloneLayoutItem,getStatics,bottom}from"react-grid-layout/core";constgravityCompactor: Compactor={type: "gravity",allowOverlap: false,compact(layout,cols){conststatics=getStatics(layout);constmaxY=100;// arbitrary max heightconstout=[];// Sort by Y descending (process bottom items first)constsorted=[...layout].sort((a,b)=>b.y-a.y);for(constitemofsorted){constl=cloneLayoutItem(item);if(!l.static){// Move down as far as possiblewhile(l.y<maxY&&!collides(l,statics)){l.y++;}l.y--;// Back up one}out.push(l);}returnout;}};// Usage<GridLayoutcompactor={gravityCompactor}/>

Example: Single Row Compactor (horizontal shelf)

constsingleRowCompactor: Compactor={type: "shelf",allowOverlap: false,compact(layout,cols){letx=0;constout=[];// Sort by original X positionconstsorted=[...layout].sort((a,b)=>a.x-b.x);for(constitemofsorted){constl=cloneLayoutItem(item);if(!l.static){l.x=x;l.y=0;// All items on row 0x+=l.w;// Wrap to next row if overflowif(x>cols){l.x=0;x=l.w;}}out.push(l);}returnout;}};

Using Helper Functions:

The core module exports helpers for building compactors:

import{resolveCompactionCollision,// Move items to resolve overlapscompactItemVertical,// Compact one item upwardcompactItemHorizontal,// Compact one item leftwardgetFirstCollision,// Find first collisioncollides,// Check if two items collidegetStatics,// Get static items from layoutcloneLayout,// Clone layout arraycloneLayoutItem// 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:

interfacePositionStrategy{/** 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

constcreateRotatedStrategy=(angleDegrees: number): PositionStrategy=>{constangleRad=(angleDegrees*Math.PI)/180;constcos=Math.cos(angleRad);constsin=Math.sin(angleRad);return{type: "rotated",scale: 1,calcStyle(pos){// Apply rotation to positionconstrotatedX=pos.left*cos-pos.top*sin;constrotatedY=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 calculationsconstx=clientX-offsetX;consty=clientY-offsetY;return{left: x*cos+y*sin,top: -x*sin+y*cos};}};};// Usage: grid inside a rotated container<divstyle={{transform: 'rotate(45deg)'}}><GridLayoutpositionStrategy={createRotatedStrategy(45)}/></div>

Example: 3D Perspective Strategy

constcreate3DStrategy=(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 foreshorteningconstperspectiveFactor=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 by @dmj900501.

import{GridBackground}from"react-grid-layout/extras";importReactGridLayout,{useContainerWidth}from"react-grid-layout";functionMyGrid(){const{ width, containerRef, mounted }=useContainerWidth();return(<divref={containerRef}style={{position: "relative"}}>{mounted&&(<><GridBackgroundwidth={width}cols={12}rowHeight={30}margin={[10,10]}rows={10}color="#f0f0f0"borderRadius={4}/><ReactGridLayoutwidth={width}gridConfig={{cols: 12,rowHeight: 30,margin: [10,10]}}>{children}</ReactGridLayout></>)}</div>);}

Props:

interfaceGridBackgroundProps{// Required - must match your GridLayout configwidth: number;// Container widthcols: number;// Number of columnsrowHeight: number;// Row height in pixels// Optionalmargin?: [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 countcolor?: string;// Cell background color (default: "#e0e0e0")borderRadius?: number;// Cell border radius (default: 4)className?: string;// Additional CSS classstyle?: 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 by @morris.

import{fastVerticalCompactor,fastHorizontalCompactor,fastVerticalOverlapCompactor,fastHorizontalOverlapCompactor}from"react-grid-layout/extras";<ReactGridLayoutcompactor={fastVerticalCompactor}// or compactor={fastHorizontalCompactor}layout={layout}width={width}/>;

Performance Benchmarks:

ItemsStandard VerticalFast VerticalSpeedup
50112 µs19 µs6x
100203 µs36 µs6x
200821 µs51 µs16x
5005.7 ms129 µs45x
ItemsStandard HorizontalFast HorizontalSpeedup
50164 µs12 µs14x
100477 µs25 µs19x
2001.1 ms42 µs26x
5009.5 ms128 µs74x

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:

import{calcGridCellDimensions}from"react-grid-layout/core";constdims=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:

functionMyGrid({ count, width }){constchildren=useMemo(()=>{returnArray.from({length: count},(_,i)=>(<divkey={i}data-grid={{x: i%12,y: Math.floor(i/12),w: 1,h: 1}}/>));},[count]);return(<ReactGridLayoutwidth={width}gridConfig={{cols: 12}}>{children}</ReactGridLayout>);}

Avoid Creating Components in Render (Legacy WidthProvider)

If using the legacy WidthProvider HOC, don't create the component during render:

importReactGridLayout,{WidthProvider}from"react-grid-layout/legacy";// Bad - creates new component every renderfunctionMyGrid(){constGridLayoutWithWidth=WidthProvider(ReactGridLayout);return<GridLayoutWithWidth>...</GridLayoutWithWidth>;}// Good - create once outside or with useMemoconstGridLayoutWithWidth=WidthProvider(ReactGridLayout);functionMyGrid(){return<GridLayoutWithWidth>...</GridLayoutWithWidth>;}

With the v2 API, use useContainerWidth hook instead to avoid this issue entirely.

Custom Child Components

Grid children must forward refs and certain props:

constCustomItem=forwardRef<HTMLDivElement,CustomItemProps>(({
style,
className,
onMouseDown,
onMouseUp,
onTouchEnd,
children,
...props},ref)=>{return(<divref={ref}style={style}className={className}onMouseDown={onMouseDown}onMouseUp={onMouseUp}onTouchEnd={onTouchEnd}>{children}</div>);});

Architecture maps

Token-lean architecture maps live in codemaps/ and are loaded at session start by AI tooling. They cover the module boundaries, public API surface, data models, and recent behavioral changes. Regenerate with /update-codemaps after substantial changes.

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 to help us easily isolate it.

About

A draggable and resizable grid layout with responsive breakpoints, for React.

Topics

Resources

Stars

22.4k stars

Watchers

228 watching

Forks

Releases

Packages

Used by

Contributors

Languages