react-grid-layout vs react-grid-system vs react-flexbox-grid
React 网格布局系统选型指南
react-grid-layoutreact-grid-systemreact-flexbox-grid类似的npm包:

React 网格布局系统选型指南

这三个库针对完全不同的布局场景。react-flexbox-grid 是早期的 Flexbox CSS 封装,react-grid-layout 专注于可拖拽调整的仪表盘布局,react-grid-system 提供现代的响应式网格组件系统。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
react-grid-layout4,040,46822,445479 kB658 天前MIT
react-grid-system51,68182888.9 kB342 年前MIT
react-flexbox-grid20,8702,909-618 年前MIT

React 网格布局方案:静态页面 vs 交互仪表盘

这三个库虽然都叫“网格”,但解决的是三个不同维度的问题。react-flexbox-grid 是早期 Flexbox 方案的 React 封装,react-grid-layout 专为可交互的仪表盘设计,而 react-grid-system 则是现代响应式页面布局的组件库。让我们从架构师的角度深入对比它们的适用场景。

🏗️ 核心布局机制:CSS 类 vs 绝对定位 vs 组件封装

react-flexbox-grid 依赖预定义的 CSS 类名来控制布局。

  • 它基于早期的 flexboxgrid.css 规范。
  • 布局逻辑隐藏在 CSS 类中,React 组件仅负责渲染结构。
  • 适合简单的静态行列分布,但缺乏灵活性。
// react-flexbox-grid: 基于 CSS 类
import { Grid, Row, Col } from 'react-flexbox-grid';

function Layout() {
  return (
    <Grid>
      <Row>
        <Col xs={12} md={6} lg={4}>
          内容区域
        </Col>
      </Row>
    </Grid>
  );
}

react-grid-layout 使用绝对定位来计算元素位置。

  • 每个子元素必须有唯一的 key 和网格坐标数据。
  • 支持动态计算位置,适合需要频繁变动的布局。
  • 不依赖外部 CSS 框架,自行管理位置状态。
// react-grid-layout: 基于绝对定位和状态
import GridLayout from 'react-grid-layout';

function Dashboard() {
  const layout = [
    { i: 'a', x: 0, y: 0, w: 2, h: 2 },
    { i: 'b', x: 2, y: 0, w: 2, h: 2 }
  ];

  return (
    <GridLayout className="layout" layout={layout} cols={12}>
      <div key="a">_widget_a</div>
      <div key="b">_widget_b</div>
    </GridLayout>
  );
}

react-grid-system 提供纯粹的 React 组件封装。

  • 使用 Container, Row, Col 等语义化组件。
  • 底层基于 Flexbox 或 CSS Grid,但对外暴露props 控制。
  • 代码更清晰,无需记忆复杂的 CSS 类名。
// react-grid-system: 基于组件 Props
import { Container, Row, Col } from 'react-grid-system';

function Page() {
  return (
    <Container>
      <Row>
        <Col xs={12} md={6} lg={4}>
          内容区域
        </Col>
      </Row>
    </Container>
  );
}

🖱️ 交互能力:静态展示 vs 动态拖拽

这是选择库时最关键的分水岭。

react-flexbox-grid 完全不支持交互。

  • 布局在渲染时固定。
  • 用户无法拖拽或调整元素大小。
  • 仅适用于内容型页面,如博客、文章页。
// react-flexbox-grid: 无交互支持
// 无法通过 props 开启拖拽功能
<Row>
  <Col xs={6}>固定位置</Col>
</Row>

react-grid-layout 核心功能就是交互。

  • 原生支持拖拽(Draggable)和调整大小(Resizable)。
  • 提供回调函数监听布局变化(onLayoutChange)。
  • 适合后台管理系统、数据看板。
// react-grid-layout: 开启交互
<GridLayout
  draggableHandle=".handle"
  resizableHandles={['s', 'w', 'e', 'n', 'sw', 'nw', 'se', 'ne']}
  onLayoutChange={handleLayoutChange}
>
  {/* 用户可拖拽这些元素 */}
</GridLayout>

react-grid-system 专注于响应式,不支持拖拽。

  • 根据屏幕宽度自动调整列宽。
  • 适合移动端适配,但不涉及用户操作布局。
  • 交互逻辑需自行通过状态管理实现。
// react-grid-system: 响应式断点
<Col xs={12} sm={6} md={4}>
  随屏幕宽度自动换行
</Col>

🛠️ 维护状态与未来兼容性

在技术选型中,库的活跃度直接影响长期维护成本。

react-flexbox-grid 已处于非活跃状态。

  • 底层 CSS 规范已过时,原生 CSS Grid 已成为标准。
  • 社区不再推荐在新项目中使用。
  • 如果遇到布局问题,很难找到最新解决方案。
// ⚠️ 警告:不建议在新项目中使用
import { Grid } from 'react-flexbox-grid'; 
// 考虑直接使用 CSS Grid 或现代组件库

react-grid-layout 维护活跃,生态成熟。

  • 广泛用于开源仪表盘项目。
  • 定期更新以支持 React 新版本。
  • 文档齐全,社区案例丰富。
// ✅ 推荐:仪表盘场景的标准选择
import GridLayout from 'react-grid-layout';
// 拥有稳定的 API 和社区支持

react-grid-system 持续更新,轻量级。

  • 专注于提供干净的网格组件。
  • 无厚重依赖,易于集成到现有项目。
  • 适合需要快速搭建响应式结构的团队。
// ✅ 推荐:标准页面布局
import { Container } from 'react-grid-system';
// 保持轻量,无多余样式污染

📊 核心差异总结

特性react-flexbox-gridreact-grid-layoutreact-grid-system
布局原理CSS 类名 (Flexbox)绝对定位 (Absolute)组件 Props (Flex/Grid)
交互支持❌ 无✅ 拖拽、调整大小❌ 仅响应式
适用场景旧项目维护仪表盘、 widgets标准响应式页面
维护状态⚠️ 非活跃/遗留✅ 活跃✅ 活跃
学习成本低 (需记 CSS 类)中 (需理解坐标)低 (语义化组件)

💡 最终选型建议

react-flexbox-grid 属于上一代技术栈。 除非你正在维护一个五年前构建的老系统,否则不应在新项目中引入它。原生 CSS Grid 和现代组件库已经提供了更好的解决方案。

react-grid-layout 是仪表盘开发的行业标准。 如果你的需求包含“用户自定义布局”、“拖拽排序”或“调整模块大小”,不要尝试自己实现,直接使用这个库。它能节省大量处理坐标计算和碰撞检测的时间。

react-grid-system 是页面结构搭建的高效工具。 当你需要快速构建一个适配手机、平板和桌面的标准网页时,它比写一堆 CSS 媒体查询更干净。它适合大多数内容展示型应用。

核心原则:先看是否需要交互。需要拖拽选 react-grid-layout;不需要拖拽只需响应式选 react-grid-system;尽量避免使用 react-flexbox-grid。

如何选择: react-grid-layout vs react-grid-system vs react-flexbox-grid

  • react-grid-layout:

    当你需要构建允许用户拖拽、调整大小的小部件仪表盘时,这是首选方案。

  • react-grid-system:

    适用于构建标准的响应式网页布局,类似于 Bootstrap 的网格系统,但作为 React 组件使用。

  • react-flexbox-grid:

    仅建议在维护基于该库的旧项目时使用。由于原生 CSS Grid 的普及,新项目不应再引入此依赖。

react-grid-layout的README

React-Grid-Layout

npm package npm 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:

import ReactGridLayout, { useContainerWidth, verticalCompactor } from 'react-grid-layout';

function MyGrid() {
  const { width, containerRef, mounted } = useContainerWidth();

  return (
    <div ref={containerRef}>
      {mounted && (
        <ReactGridLayout
          width={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:

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

Quick Start

import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";

function MyGrid() {
  const { width, containerRef, mounted } = useContainerWidth();

  const layout = [
    { 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 (
    <div ref={containerRef}>
      {mounted && (
        <ReactGridLayout
          layout={layout}
          width={width}
          gridConfig={{ cols: 12, rowHeight: 30 }}
        >
          <div key="a">a</div>
          <div key="b">b</div>
          <div key="c">c</div>
        </ReactGridLayout>
      )}
    </div>
  );
}

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

<ReactGridLayout width={width} gridConfig={{ cols: 12, rowHeight: 30 }}>
  <div key="a" data-grid={{ x: 0, y: 0, w: 1, h: 2, static: true }}>
    a
  </div>
  <div key="b" data-grid={{ x: 1, y: 0, w: 3, h: 2 }}>
    b
  </div>
  <div key="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";

function MyResponsiveGrid() {
  const { width, containerRef, mounted } = useContainerWidth();

  const layouts = {
    lg: [{ i: "1", x: 0, y: 0, w: 2, h: 2 }],
    md: [{ i: "1", x: 0, y: 0, w: 2, h: 2 }]
  };

  return (
    <div ref={containerRef}>
      {mounted && (
        <Responsive
          layouts={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}
        >
          <div key="1">1</div>
          <div key="2">2</div>
          <div key="3">3</div>
        </Responsive>
      )}
    </div>
  );
}

Providing Grid Width

The width prop is required. You have several options:

Option 1: useContainerWidth Hook (Recommended)

import ReactGridLayout, { useContainerWidth } from "react-grid-layout";

function MyGrid() {
  const { width, containerRef, mounted } = useContainerWidth();

  return (
    <div ref={containerRef}>
      {mounted && <ReactGridLayout width={width}>...</ReactGridLayout>}
    </div>
  );
}

Option 2: Fixed Width

<ReactGridLayout width={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:

import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";

const GridLayoutWithWidth = WidthProvider(ReactGridLayout);

function MyGrid() {
  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";

function MyGrid() {
  const { width, containerRef, mounted, measureWidth } = useContainerWidth({
    measureBeforeMount: false, // Set true for SSR
    initialWidth: 1280 // Width before first measurement
  });

  return (
    <div ref={containerRef}>{mounted && <ReactGridLayout width={width} />}</div>
  );
}

Type Definitions:

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<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";

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 (
    <div style={{ height: containerHeight * rowHeight }}>
      {layout.map(item => (
        <div
          key={item.i}
          onMouseDown={() => onDragStart(item.i, item.x, item.y)}
        >
          {item.i}
        </div>
      ))}
      {placeholder && <div className="placeholder" />}
    </div>
  );
}

Type Definitions:

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
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 (
    <div ref={containerRef}>
      <div>
        Current breakpoint: {breakpoint} ({cols} columns)
      </div>
      {mounted && (
        <GridLayout width={width} cols={cols} layout={layout}>
          {/* children */}
        </GridLayout>
      )}
    </div>
  );
}

Type Definitions:

interface UseResponsiveLayoutOptions<B extends string = 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;
}

interface UseResponsiveLayoutResult<B extends string = 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[];
}

type DefaultBreakpoints = "lg" | "md" | "sm" | "xs" | "xxs";

API Reference

ReactGridLayout Props

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

interface ReactGridLayoutProps {
  // Required
  children: React.ReactNode;
  width: number; // Container width in pixels

  // Configuration interfaces (see below for details)
  gridConfig?: Partial<GridConfig>; // Grid measurement settings
  dragConfig?: Partial<DragConfig>; // Drag behavior settings
  resizeConfig?: Partial<ResizeConfig>; // Resize behavior settings
  dropConfig?: Partial<DropConfig>; // External drop settings
  positionStrategy?: PositionStrategy; // CSS positioning strategy
  compactor?: Compactor; // Layout compaction strategy

  // Layout data
  layout?: Layout; // Layout definition
  droppingItem?: LayoutItem; // Item configuration when dropping from outside

  // Container
  autoSize?: boolean; // Auto-size container height (default: true)
  className?: string;
  style?: React.CSSProperties;
  innerRef?: React.Ref<HTMLDivElement>;

  // Callbacks
  onLayoutChange?: (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:

interface GridConfig {
  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:

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

ResizeConfig

Resize behavior configuration:

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

DropConfig

External drop configuration:

interface DropConfig {
  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 transforms
  absoluteStrategy, // Use top/left positioning
  createScaledStrategy // For scaled containers
} from "react-grid-layout/core";

// Example: scaled container
<div style={{ transform: 'scale(0.5)' }}>
  <ReactGridLayout positionStrategy={createScaledStrategy(0.5)} ... />
</div>

Compactor

Layout compaction strategy. Built-in options:

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:

interface ResponsiveGridLayoutProps<B extends string = string> {
  // Responsive configuration
  breakpoint?: 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-breakpoint
  margin?: [number, number] | Partial<Record<B, [number, number]>>;
  containerPadding?:
    | [number, number]
    | Partial<Record<B, [number, number] | null>>
    | null;

  // Callbacks
  onBreakpointChange?: (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

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:

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:

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)

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
<GridLayout compactor={gravityCompactor} />

Example: Single Row Compactor (horizontal shelf)

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:

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:

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

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
<div style={{ transform: 'rotate(45deg)' }}>
  <GridLayout positionStrategy={createRotatedStrategy(45)} />
</div>

Example: 3D Perspective Strategy

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 by @dmj900501.

import { GridBackground } from "react-grid-layout/extras";
import ReactGridLayout, { useContainerWidth } from "react-grid-layout";

function MyGrid() {
  const { width, containerRef, mounted } = useContainerWidth();

  return (
    <div ref={containerRef} style={{ position: "relative" }}>
      {mounted && (
        <>
          <GridBackground
            width={width}
            cols={12}
            rowHeight={30}
            margin={[10, 10]}
            rows={10}
            color="#f0f0f0"
            borderRadius={4}
          />
          <ReactGridLayout
            width={width}
            gridConfig={{ cols: 12, rowHeight: 30, margin: [10, 10] }}
          >
            {children}
          </ReactGridLayout>
        </>
      )}
    </div>
  );
}

Props:

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 by @morris.

import {
  fastVerticalCompactor,
  fastHorizontalCompactor,
  fastVerticalOverlapCompactor,
  fastHorizontalOverlapCompactor
} from "react-grid-layout/extras";

<ReactGridLayout
  compactor={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";

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:

function MyGrid({ count, width }) {
  const children = useMemo(() => {
    return Array.from({ length: count }, (_, i) => (
      <div
        key={i}
        data-grid={{ x: i % 12, y: Math.floor(i / 12), w: 1, h: 1 }}
      />
    ));
  }, [count]);

  return (
    <ReactGridLayout width={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:

import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";

// Bad - creates new component every render
function MyGrid() {
  const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
  return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
}

// Good - create once outside or with useMemo
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);

function MyGrid() {
  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:

const CustomItem = forwardRef<HTMLDivElement, CustomItemProps>(
  (
    {
      style,
      className,
      onMouseDown,
      onMouseUp,
      onTouchEnd,
      children,
      ...props
    },
    ref
  ) => {
    return (
      <div
        ref={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.