react-grid-layout 1.5.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +1156 -442
  2. package/css/styles.css +5 -4
  3. package/dist/ResponsiveGridLayout-BkHF9YHa.d.mts +133 -0
  4. package/dist/ResponsiveGridLayout-CLn16-X3.d.ts +133 -0
  5. package/dist/calculate-DsVTldEE.d.ts +196 -0
  6. package/dist/calculate-DwbL1D06.d.mts +196 -0
  7. package/dist/chunk-2O3ZME4Q.mjs +703 -0
  8. package/dist/chunk-2O3ZME4Q.mjs.map +1 -0
  9. package/dist/chunk-526JND3R.mjs +3 -0
  10. package/dist/chunk-526JND3R.mjs.map +1 -0
  11. package/dist/chunk-7ELO5FRW.js +4 -0
  12. package/dist/chunk-7ELO5FRW.js.map +1 -0
  13. package/dist/chunk-AWM66AWF.mjs +422 -0
  14. package/dist/chunk-AWM66AWF.mjs.map +1 -0
  15. package/dist/chunk-BJFPTW5Q.js +449 -0
  16. package/dist/chunk-BJFPTW5Q.js.map +1 -0
  17. package/dist/chunk-FN6MIZ24.js +744 -0
  18. package/dist/chunk-FN6MIZ24.js.map +1 -0
  19. package/dist/chunk-GPZBANOK.js +435 -0
  20. package/dist/chunk-GPZBANOK.js.map +1 -0
  21. package/dist/chunk-LWF5EYMO.mjs +429 -0
  22. package/dist/chunk-LWF5EYMO.mjs.map +1 -0
  23. package/dist/chunk-MXXN7GUE.mjs +1408 -0
  24. package/dist/chunk-MXXN7GUE.mjs.map +1 -0
  25. package/dist/chunk-QRW3ND4U.js +1417 -0
  26. package/dist/chunk-QRW3ND4U.js.map +1 -0
  27. package/dist/core.d.mts +186 -0
  28. package/dist/core.d.ts +186 -0
  29. package/dist/core.js +274 -0
  30. package/dist/core.js.map +1 -0
  31. package/dist/core.mjs +5 -0
  32. package/dist/core.mjs.map +1 -0
  33. package/dist/extras.d.mts +208 -0
  34. package/dist/extras.d.ts +208 -0
  35. package/dist/extras.js +462 -0
  36. package/dist/extras.js.map +1 -0
  37. package/dist/extras.mjs +454 -0
  38. package/dist/extras.mjs.map +1 -0
  39. package/dist/index.d.mts +7 -0
  40. package/dist/index.d.ts +7 -0
  41. package/dist/index.js +154 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/index.mjs +7 -0
  44. package/dist/index.mjs.map +1 -0
  45. package/dist/legacy.d.mts +163 -0
  46. package/dist/legacy.d.ts +163 -0
  47. package/dist/legacy.js +320 -0
  48. package/dist/legacy.js.map +1 -0
  49. package/dist/legacy.mjs +308 -0
  50. package/dist/legacy.mjs.map +1 -0
  51. package/dist/position-C8a5g6bb.d.mts +324 -0
  52. package/dist/position-H8tBL91b.d.ts +324 -0
  53. package/dist/react.d.mts +357 -0
  54. package/dist/react.d.ts +357 -0
  55. package/dist/react.js +96 -0
  56. package/dist/react.js.map +1 -0
  57. package/dist/react.mjs +7 -0
  58. package/dist/react.mjs.map +1 -0
  59. package/dist/responsive-BB5d98xz.d.mts +145 -0
  60. package/dist/responsive-BXI-GCXG.d.ts +145 -0
  61. package/dist/types-CokovIMH.d.mts +469 -0
  62. package/dist/types-CokovIMH.d.ts +469 -0
  63. package/index-dev.js +24 -0
  64. package/package.json +133 -40
  65. package/.babelrc.js +0 -22
  66. package/.browserslistrc +0 -3
  67. package/.eslintignore +0 -4
  68. package/.flowconfig +0 -20
  69. package/.prettierignore +0 -4
  70. package/.prettierrc +0 -17
  71. package/CHANGELOG.md +0 -985
  72. package/build/GridItem.js +0 -638
  73. package/build/ReactGridLayout.js +0 -741
  74. package/build/ReactGridLayoutPropTypes.js +0 -210
  75. package/build/ResponsiveReactGridLayout.js +0 -297
  76. package/build/calculateUtils.js +0 -165
  77. package/build/components/WidthProvider.js +0 -115
  78. package/build/fastRGLPropsEqual.js +0 -5
  79. package/build/responsiveUtils.js +0 -101
  80. package/build/utils.js +0 -842
  81. package/dist/react-grid-layout.min.js +0 -2
  82. package/dist/react-grid-layout.min.js.map +0 -1
  83. package/index.js.flow +0 -8
  84. package/ip_fetcher +0 -0
  85. package/ip_fetcher.c +0 -47
  86. package/lib/GridItem.jsx +0 -689
  87. package/lib/ReactGridLayout.jsx +0 -855
  88. package/lib/ReactGridLayoutPropTypes.js +0 -241
  89. package/lib/ResponsiveReactGridLayout.jsx +0 -349
  90. package/lib/calculateUtils.js +0 -169
  91. package/lib/components/WidthProvider.jsx +0 -110
  92. package/lib/fastRGLPropsEqual.js +0 -46
  93. package/lib/responsiveUtils.js +0 -118
  94. package/lib/utils.js +0 -995
  95. package/yarn-error.log +0 -7780
package/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # React-Grid-Layout
2
2
 
3
- [![travis build](https://travis-ci.org/STRML/react-grid-layout.svg?branch=master)](https://travis-ci.org/STRML/react-grid-layout)
4
- [![CDNJS](https://img.shields.io/cdnjs/v/react-grid-layout.svg)](https://cdnjs.com/libraries/react-grid-layout)
5
3
  [![npm package](https://img.shields.io/npm/v/react-grid-layout.svg?style=flat-square)](https://www.npmjs.org/package/react-grid-layout)
6
4
  [![npm downloads](https://img.shields.io/npm/dt/react-grid-layout.svg?maxAge=2592000)]()
7
5
 
@@ -21,19 +19,97 @@ RGL is React-only and does not require jQuery.
21
19
 
22
20
  ## Table of Contents
23
21
 
22
+ - [What's New in v2](#whats-new-in-v2)
23
+ - [Migrating from v1](#migrating-from-v1)
24
24
  - [Demos](#demos)
25
25
  - [Features](#features)
26
26
  - [Installation](#installation)
27
- - [Usage](#usage)
27
+ - [Quick Start](#quick-start)
28
28
  - [Responsive Usage](#responsive-usage)
29
29
  - [Providing Grid Width](#providing-grid-width)
30
- - [Grid Layout Props](#grid-layout-props)
31
- - [Responsive Grid Layout Props](#responsive-grid-layout-props)
32
- - [Grid Item Props](#grid-item-props)
33
- - [User Recipes](../../wiki/Users-recipes)
30
+ - [Hooks API](#hooks-api)
31
+ - [API Reference](#api-reference)
32
+ - [Extending: Custom Compactors & Position Strategies](#extending-custom-compactors--position-strategies)
33
+ - [Extras](#extras)
34
34
  - [Performance](#performance)
35
35
  - [Contribute](#contribute)
36
- - [TODO List](#todo-list)
36
+
37
+ ## What's New in v2
38
+
39
+ Version 2 is a complete TypeScript rewrite with a modernized API:
40
+
41
+ - **Full TypeScript support** - First-class types, no more `@types/react-grid-layout`
42
+ - **React Hooks** - New `useContainerWidth`, `useGridLayout`, and `useResponsiveLayout` hooks
43
+ - **Composable Configuration** - Group related props into focused interfaces:
44
+ - `gridConfig` - cols, rowHeight, margin, padding
45
+ - `dragConfig` - enable, handle, cancel, bounded
46
+ - `resizeConfig` - enable, handles
47
+ - `positionStrategy` - transform vs absolute positioning
48
+ - `compactor` - vertical, horizontal, or custom algorithms
49
+ - **Modular architecture** - Import only what you need:
50
+ - `react-grid-layout` - React components and hooks (v2 API)
51
+ - `react-grid-layout/core` - Pure layout algorithms (framework-agnostic)
52
+ - `react-grid-layout/legacy` - v1 flat props API for migration
53
+ - `react-grid-layout/extras` - Optional components like `GridBackground`
54
+ - **Smaller bundle** - Tree-shakeable ESM and CJS builds
55
+
56
+ ### Breaking Changes
57
+
58
+ See the [RFC](./rfcs/0001-v2-typescript-rewrite.md#breaking-changes-in-v2) for detailed migration examples.
59
+
60
+ | Change | Description |
61
+ | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
62
+ | [`width` prop required](./rfcs/0001-v2-typescript-rewrite.md#breaking-changes-in-v2) | Use `useContainerWidth` hook or provide your own measurement |
63
+ | [`onDragStart` threshold](./rfcs/0001-v2-typescript-rewrite.md#1-ondragstart-no-longer-fires-on-click-only-events) | Now fires after 3px movement, not on mousedown. Use `onMouseDown` for immediate response |
64
+ | [Immutable callbacks](./rfcs/0001-v2-typescript-rewrite.md#2-immutable-callback-parameters) | Callback parameters are read-only. Use `onLayoutChange` or constraints instead of mutation |
65
+ | [`data-grid` in legacy only](./rfcs/0001-v2-typescript-rewrite.md#3-data-grid-prop-only-available-in-legacy-wrapper) | v2 requires explicit `layout` prop. Use legacy wrapper for `data-grid` |
66
+ | [Pluggable compaction](./rfcs/0001-v2-typescript-rewrite.md#4-pluggable-compaction-algorithms) | Compaction is now pluggable via `Compactor` interface. Optional fast O(n log n) algorithm in `/extras` |
67
+ | UMD bundle removed | Use a bundler (Vite, webpack, esbuild) |
68
+ | `verticalCompact` removed | Use `compactType={null}` or `compactor={noCompactor}` |
69
+
70
+ ## Migrating from v1
71
+
72
+ **Quick migration** - change your import to use the legacy wrapper:
73
+
74
+ ```diff
75
+ - import GridLayout, { Responsive, WidthProvider } from 'react-grid-layout';
76
+ + import GridLayout, { Responsive, WidthProvider } from 'react-grid-layout/legacy';
77
+ ```
78
+
79
+ This provides **100% API compatibility** with v1.
80
+
81
+ **Full migration** - adopt the v2 API for new features and better tree-shaking:
82
+
83
+ ```typescript
84
+ import ReactGridLayout, { useContainerWidth, verticalCompactor } from 'react-grid-layout';
85
+
86
+ function MyGrid() {
87
+ const { width, containerRef, mounted } = useContainerWidth();
88
+
89
+ return (
90
+ <div ref={containerRef}>
91
+ {mounted && (
92
+ <ReactGridLayout
93
+ width={width}
94
+ layout={layout}
95
+ gridConfig={{ cols: 12, rowHeight: 30 }}
96
+ dragConfig={{ enabled: true, handle: '.handle' }}
97
+ compactor={verticalCompactor}
98
+ >
99
+ {children}
100
+ </ReactGridLayout>
101
+ )}
102
+ </div>
103
+ );
104
+ }
105
+ ```
106
+
107
+ | Use Case | Recommendation |
108
+ | -------------------- | ---------------------------------- |
109
+ | Existing v1 codebase | `react-grid-layout/legacy` |
110
+ | New project | v2 API with hooks |
111
+ | Custom compaction | v2 with custom `Compactor` |
112
+ | SSR | v2 with `measureBeforeMount: true` |
37
113
 
38
114
  ## Demos
39
115
 
@@ -48,17 +124,17 @@ RGL is React-only and does not require jQuery.
48
124
  1. [Saving a Responsive Layout to LocalStorage](https://react-grid-layout.github.io/react-grid-layout/examples/08-localstorage-responsive.html)
49
125
  1. [Minimum and Maximum Width/Height](https://react-grid-layout.github.io/react-grid-layout/examples/09-min-max-wh.html)
50
126
  1. [Dynamic Minimum and Maximum Width/Height](https://react-grid-layout.github.io/react-grid-layout/examples/10-dynamic-min-max-wh.html)
51
- 1. [No Vertical Compacting (Free Movement)](https://react-grid-layout.github.io/react-grid-layout/examples/11-no-vertical-compact.html)
52
- 1. [Prevent Collision](https://react-grid-layout.github.io/react-grid-layout/examples/12-prevent-collision.html)
53
- 1. [Error Case](https://react-grid-layout.github.io/react-grid-layout/examples/13-error-case.html)
54
- 1. [Toolbox](https://react-grid-layout.github.io/react-grid-layout/examples/14-toolbox.html)
55
- 1. [Drag From Outside](https://react-grid-layout.github.io/react-grid-layout/examples/15-drag-from-outside.html)
56
- 1. [Bounded Layout](https://react-grid-layout.github.io/react-grid-layout/examples/16-bounded.html)
57
- 1. [Responsive Bootstrap-style Layout](https://react-grid-layout.github.io/react-grid-layout/examples/17-responsive-bootstrap-style.html)
58
- 1. [Scaled Containers](https://react-grid-layout.github.io/react-grid-layout/examples/18-scale.html)
59
- 1. [Allow Overlap](https://react-grid-layout.github.io/react-grid-layout/examples/19-allow-overlap.html)
60
- 1. [All Resizable Handles](https://react-grid-layout.github.io/react-grid-layout/examples/20-resizable-handles.html)
61
- 1. [Single Row Horizontal](https://react-grid-layout.github.io/react-grid-layout/examples/21-horizontal.html)
127
+ 1. [Toolbox](https://react-grid-layout.github.io/react-grid-layout/examples/11-toolbox.html)
128
+ 1. [Drag From Outside](https://react-grid-layout.github.io/react-grid-layout/examples/12-drag-from-outside.html)
129
+ 1. [Bounded Layout](https://react-grid-layout.github.io/react-grid-layout/examples/13-bounded.html)
130
+ 1. [Responsive Bootstrap-style Layout](https://react-grid-layout.github.io/react-grid-layout/examples/14-responsive-bootstrap-style.html)
131
+ 1. [Scaled Containers](https://react-grid-layout.github.io/react-grid-layout/examples/15-scale.html)
132
+ 1. [Allow Overlap](https://react-grid-layout.github.io/react-grid-layout/examples/16-allow-overlap.html)
133
+ 1. [All Resizable Handles](https://react-grid-layout.github.io/react-grid-layout/examples/17-resizable-handles.html)
134
+ 1. [Compactor Showcase](https://react-grid-layout.github.io/react-grid-layout/examples/18-compactors.html)
135
+ 1. [Pluggable Constraints](https://react-grid-layout.github.io/react-grid-layout/examples/19-constraints.html)
136
+ 1. [Aspect Ratio Constraints](https://react-grid-layout.github.io/react-grid-layout/examples/20-aspect-ratio.html)
137
+ 1. [Custom Constraints](https://react-grid-layout.github.io/react-grid-layout/examples/21-custom-constraints.html)
62
138
 
63
139
  #### Projects Using React-Grid-Layout
64
140
 
@@ -68,25 +144,15 @@ RGL is React-only and does not require jQuery.
68
144
  - [Grafana](https://grafana.com/)
69
145
  - [Metabase](http://www.metabase.com/)
70
146
  - [HubSpot](http://www.hubspot.com)
71
- - [ComNetViz](http://www.grotto-networking.com/ComNetViz/ComNetViz.html)
72
- - [Stoplight](https://app.stoplight.io)
73
- - [Reflect](https://reflect.io)
74
- - [ez-Dashing](https://github.com/ylacaute/ez-Dashing)
75
147
  - [Kibana](https://www.elastic.co/products/kibana)
76
- - [Graphext](https://graphext.com/)
77
148
  - [Monday](https://support.monday.com/hc/en-us/articles/360002187819-What-are-the-Dashboards-)
78
- - [Quadency](https://quadency.com/)
79
- - [Hakkiri](https://www.hakkiri.io)
80
- - [Ubidots](https://help.ubidots.com/en/articles/2400308-create-dashboards-and-widgets)
81
- - [Statsout](https://statsout.com/)
82
- - [Datto RMM](https://www.datto.com/uk/products/rmm/)
83
- - [SquaredUp](https://squaredup.com/)
84
149
 
85
150
  _Know of others? Create a PR to let me know!_
86
151
 
87
152
  ## Features
88
153
 
89
154
  - 100% React - no jQuery
155
+ - Full TypeScript support
90
156
  - Compatible with server-rendered apps
91
157
  - Draggable widgets
92
158
  - Resizable widgets
@@ -98,506 +164,1170 @@ _Know of others? Create a PR to let me know!_
98
164
  - Responsive breakpoints
99
165
  - Separate layouts per responsive breakpoint
100
166
  - Grid Items placed using CSS Transforms
101
- - Performance with CSS Transforms: [on](http://i.imgur.com/FTogpLp.jpg) / [off](http://i.imgur.com/gOveMm8.jpg), note paint (green) as % of time
102
167
  - Compatibility with `<React.StrictMode>`
103
168
 
104
- | Version | Compatibility |
105
- | ------------ | --------------- |
106
- | >= 0.17.0 | React 16 & 17 |
107
- | >= 0.11.3 | React 0.14 & 15 |
108
- | >= 0.10.0 | React 0.14 |
109
- | 0.8. - 0.9.2 | React 0.13 |
110
- | < 0.8 | React 0.12 |
169
+ | Version | Compatibility |
170
+ | --------- | --------------------- |
171
+ | >= 2.0.0 | React 18+, TypeScript |
172
+ | >= 0.17.0 | React 16 & 17 |
111
173
 
112
174
  ## Installation
113
175
 
114
- Install the React-Grid-Layout [package](https://www.npmjs.org/package/react-grid-layout) using [npm](https://www.npmjs.com/):
115
-
116
176
  ```bash
117
177
  npm install react-grid-layout
118
178
  ```
119
179
 
120
- Include the following stylesheets in your application:
180
+ Include the stylesheets in your application:
121
181
 
182
+ ```js
183
+ import "react-grid-layout/css/styles.css";
184
+ import "react-resizable/css/styles.css";
122
185
  ```
123
- /node_modules/react-grid-layout/css/styles.css
124
- /node_modules/react-resizable/css/styles.css
186
+
187
+ Or link them directly:
188
+
189
+ ```html
190
+ <link rel="stylesheet" href="/node_modules/react-grid-layout/css/styles.css" />
191
+ <link rel="stylesheet" href="/node_modules/react-resizable/css/styles.css" />
125
192
  ```
126
193
 
127
- ## Usage
194
+ ## Quick Start
128
195
 
129
- Use ReactGridLayout like any other component. The following example below will
130
- produce a grid with three items where:
196
+ ```tsx
197
+ import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
198
+ import "react-grid-layout/css/styles.css";
199
+ import "react-resizable/css/styles.css";
131
200
 
132
- - users will not be able to drag or resize item `a`
133
- - item `b` will be restricted to a minimum width of 2 grid blocks and a maximum width of 4 grid blocks
134
- - users will be able to freely drag and resize item `c`
201
+ function MyGrid() {
202
+ const { width, containerRef, mounted } = useContainerWidth();
135
203
 
136
- ```js
137
- import GridLayout from "react-grid-layout";
138
-
139
- class MyFirstGrid extends React.Component {
140
- render() {
141
- // layout is an array of objects, see the demo for more complete usage
142
- const layout = [
143
- { i: "a", x: 0, y: 0, w: 1, h: 2, static: true },
144
- { i: "b", x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
145
- { i: "c", x: 4, y: 0, w: 1, h: 2 }
146
- ];
147
- return (
148
- <GridLayout
149
- className="layout"
150
- layout={layout}
151
- cols={12}
152
- rowHeight={30}
153
- width={1200}
154
- >
155
- <div key="a">a</div>
156
- <div key="b">b</div>
157
- <div key="c">c</div>
158
- </GridLayout>
159
- );
160
- }
204
+ const layout = [
205
+ { i: "a", x: 0, y: 0, w: 1, h: 2, static: true },
206
+ { i: "b", x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
207
+ { i: "c", x: 4, y: 0, w: 1, h: 2 }
208
+ ];
209
+
210
+ return (
211
+ <div ref={containerRef}>
212
+ {mounted && (
213
+ <ReactGridLayout
214
+ layout={layout}
215
+ width={width}
216
+ gridConfig={{ cols: 12, rowHeight: 30 }}
217
+ >
218
+ <div key="a">a</div>
219
+ <div key="b">b</div>
220
+ <div key="c">c</div>
221
+ </ReactGridLayout>
222
+ )}
223
+ </div>
224
+ );
161
225
  }
162
226
  ```
163
227
 
164
- You may also choose to set layout properties directly on the children:
228
+ You can also define layout on children using `data-grid`:
229
+
230
+ ```tsx
231
+ <ReactGridLayout width={width} gridConfig={{ cols: 12, rowHeight: 30 }}>
232
+ <div key="a" data-grid={{ x: 0, y: 0, w: 1, h: 2, static: true }}>
233
+ a
234
+ </div>
235
+ <div key="b" data-grid={{ x: 1, y: 0, w: 3, h: 2 }}>
236
+ b
237
+ </div>
238
+ <div key="c" data-grid={{ x: 4, y: 0, w: 1, h: 2 }}>
239
+ c
240
+ </div>
241
+ </ReactGridLayout>
242
+ ```
165
243
 
166
- ```js
167
- import GridLayout from "react-grid-layout";
244
+ ## Responsive Usage
168
245
 
169
- class MyFirstGrid extends React.Component {
170
- render() {
171
- return (
172
- <GridLayout className="layout" cols={12} rowHeight={30} width={1200}>
173
- <div key="a" data-grid={{ x: 0, y: 0, w: 1, h: 2, static: true }}>
174
- a
175
- </div>
176
- <div key="b" data-grid={{ x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 }}>
177
- b
178
- </div>
179
- <div key="c" data-grid={{ x: 4, y: 0, w: 1, h: 2 }}>
180
- c
181
- </div>
182
- </GridLayout>
183
- );
184
- }
246
+ Use `Responsive` for automatic breakpoint handling:
247
+
248
+ ```tsx
249
+ import { Responsive, useContainerWidth } from "react-grid-layout";
250
+
251
+ function MyResponsiveGrid() {
252
+ const { width, containerRef, mounted } = useContainerWidth();
253
+
254
+ const layouts = {
255
+ lg: [{ i: "1", x: 0, y: 0, w: 2, h: 2 }],
256
+ md: [{ i: "1", x: 0, y: 0, w: 2, h: 2 }]
257
+ };
258
+
259
+ return (
260
+ <div ref={containerRef}>
261
+ {mounted && (
262
+ <Responsive
263
+ layouts={layouts}
264
+ breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
265
+ cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
266
+ width={width}
267
+ >
268
+ <div key="1">1</div>
269
+ <div key="2">2</div>
270
+ <div key="3">3</div>
271
+ </Responsive>
272
+ )}
273
+ </div>
274
+ );
185
275
  }
186
276
  ```
187
277
 
188
- ### Usage without Browserify/Webpack
278
+ ## Providing Grid Width
189
279
 
190
- A module usable in a `<script>` tag is included [here](/dist/react-grid-layout.min.js). It uses a UMD shim and
191
- excludes `React`, so it must be otherwise available in your application, either via RequireJS or on `window.React`.
280
+ The `width` prop is required. You have several options:
192
281
 
193
- ### Responsive Usage
282
+ ### Option 1: useContainerWidth Hook (Recommended)
194
283
 
195
- To make RGL responsive, use the `<ResponsiveReactGridLayout>` element:
284
+ ```tsx
285
+ import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
196
286
 
197
- ```js
198
- import { Responsive as ResponsiveGridLayout } from "react-grid-layout";
287
+ function MyGrid() {
288
+ const { width, containerRef, mounted } = useContainerWidth();
199
289
 
200
- class MyResponsiveGrid extends React.Component {
201
- render() {
202
- // {lg: layout1, md: layout2, ...}
203
- const layouts = getLayoutsFromSomewhere();
204
- return (
205
- <ResponsiveGridLayout
206
- className="layout"
207
- layouts={layouts}
208
- breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
209
- cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
210
- >
211
- <div key="1">1</div>
212
- <div key="2">2</div>
213
- <div key="3">3</div>
214
- </ResponsiveGridLayout>
215
- );
216
- }
290
+ return (
291
+ <div ref={containerRef}>
292
+ {mounted && <ReactGridLayout width={width}>...</ReactGridLayout>}
293
+ </div>
294
+ );
217
295
  }
218
296
  ```
219
297
 
220
- When in responsive mode, you should supply at least one breakpoint via the `layouts` property.
298
+ ### Option 2: Fixed Width
299
+
300
+ ```tsx
301
+ <ReactGridLayout width={1200}>...</ReactGridLayout>
302
+ ```
303
+
304
+ ### Option 3: CSS Container Queries or ResizeObserver
221
305
 
222
- When using `layouts`, it is best to supply as many breakpoints as possible, especially the largest one.
223
- If the largest is provided, RGL will attempt to interpolate the rest.
306
+ Use any width measurement library like [react-sizeme](https://github.com/ctrlplusb/react-sizeme) or your own ResizeObserver implementation.
224
307
 
225
- You will also need to provide a `width`, when using `<ResponsiveReactGridLayout>` it is suggested you use the HOC
226
- `WidthProvider` as per the instructions below.
308
+ ### Option 4: Legacy WidthProvider HOC
227
309
 
228
- It is possible to supply default mappings via the `data-grid` property on individual
229
- items, so that they would be taken into account within layout interpolation.
310
+ For backwards compatibility, you can still use `WidthProvider`:
230
311
 
231
- ### Providing Grid Width
312
+ ```tsx
313
+ import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";
232
314
 
233
- Both `<ResponsiveReactGridLayout>` and `<ReactGridLayout>` take `width` to calculate
234
- positions on drag events. In simple cases a HOC `WidthProvider` can be used to automatically determine
235
- width upon initialization and window resize events.
315
+ const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
236
316
 
237
- ```js
238
- import { Responsive, WidthProvider } from "react-grid-layout";
317
+ function MyGrid() {
318
+ return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
319
+ }
320
+ ```
239
321
 
240
- const ResponsiveGridLayout = WidthProvider(Responsive);
322
+ ## Hooks API
241
323
 
242
- class MyResponsiveGrid extends React.Component {
243
- render() {
244
- // {lg: layout1, md: layout2, ...}
245
- var layouts = getLayoutsFromSomewhere();
246
- return (
247
- <ResponsiveGridLayout
248
- className="layout"
249
- layouts={layouts}
250
- breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
251
- cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
252
- >
253
- <div key="1">1</div>
254
- <div key="2">2</div>
255
- <div key="3">3</div>
256
- </ResponsiveGridLayout>
257
- );
324
+ The v2 API provides three hooks for different use cases. Choose based on your needs:
325
+
326
+ | Hook | Use When |
327
+ | --------------------- | -------------------------------------------------------------------- |
328
+ | `useContainerWidth` | You need responsive width measurement (most common) |
329
+ | `useGridLayout` | You're building a custom grid component or need direct state control |
330
+ | `useResponsiveLayout` | You're building a custom responsive grid with breakpoint logic |
331
+
332
+ ### useContainerWidth
333
+
334
+ Observes container width using ResizeObserver and provides reactive width updates. This is the recommended way to provide width to the grid.
335
+
336
+ **Why use it instead of WidthProvider?**
337
+
338
+ - Hooks are more composable and easier to test
339
+ - No HOC wrapper means simpler component tree
340
+ - Explicit control over when to render (via `mounted`)
341
+ - Works better with SSR
342
+
343
+ ```tsx
344
+ import { useContainerWidth } from "react-grid-layout";
345
+
346
+ function MyGrid() {
347
+ const { width, containerRef, mounted, measureWidth } = useContainerWidth({
348
+ measureBeforeMount: false, // Set true for SSR
349
+ initialWidth: 1280 // Width before first measurement
350
+ });
351
+
352
+ return (
353
+ <div ref={containerRef}>{mounted && <ReactGridLayout width={width} />}</div>
354
+ );
355
+ }
356
+ ```
357
+
358
+ **Type Definitions:**
359
+
360
+ ```ts
361
+ interface UseContainerWidthOptions {
362
+ /** Delay render until width is measured. Useful for SSR. Default: false */
363
+ measureBeforeMount?: boolean;
364
+ /** Initial width before measurement. Default: 1280 */
365
+ initialWidth?: number;
366
+ }
367
+
368
+ interface UseContainerWidthResult {
369
+ /** Current container width in pixels */
370
+ width: number;
371
+ /** Whether the container has been measured at least once */
372
+ mounted: boolean;
373
+ /** Ref to attach to the container element */
374
+ containerRef: RefObject<HTMLDivElement | null>;
375
+ /** Manually trigger a width measurement */
376
+ measureWidth: () => void;
377
+ }
378
+ ```
379
+
380
+ ### useGridLayout
381
+
382
+ Core layout state management hook. Use this when you need direct control over drag/resize/drop state, or when building a custom grid component.
383
+
384
+ **Why use it instead of the component?**
385
+
386
+ - Full control over layout state and updates
387
+ - Access to drag/resize/drop state for custom UIs
388
+ - Can integrate with external state management
389
+ - Build headless grid implementations
390
+
391
+ ```tsx
392
+ import { useGridLayout } from "react-grid-layout";
393
+
394
+ function CustomGrid({ initialLayout }) {
395
+ const {
396
+ layout,
397
+ setLayout,
398
+ dragState,
399
+ resizeState,
400
+ onDragStart,
401
+ onDrag,
402
+ onDragStop,
403
+ onResizeStart,
404
+ onResize,
405
+ onResizeStop,
406
+ containerHeight,
407
+ isInteracting,
408
+ compactor
409
+ } = useGridLayout({
410
+ layout: initialLayout,
411
+ cols: 12,
412
+ compactType: "vertical",
413
+ allowOverlap: false,
414
+ preventCollision: false,
415
+ onLayoutChange: newLayout => console.log("Layout changed:", newLayout)
416
+ });
417
+
418
+ // Access drag state for custom placeholder rendering
419
+ const placeholder = dragState.activeDrag;
420
+
421
+ // Check if any interaction is happening
422
+ if (isInteracting) {
423
+ // Disable other UI during drag/resize
258
424
  }
425
+
426
+ return (
427
+ <div style={{ height: containerHeight * rowHeight }}>
428
+ {layout.map(item => (
429
+ <div
430
+ key={item.i}
431
+ onMouseDown={() => onDragStart(item.i, item.x, item.y)}
432
+ >
433
+ {item.i}
434
+ </div>
435
+ ))}
436
+ {placeholder && <div className="placeholder" />}
437
+ </div>
438
+ );
259
439
  }
260
440
  ```
261
441
 
262
- This allows you to easily replace `WidthProvider` with your own Provider HOC if you need more sophisticated logic.
442
+ **Type Definitions:**
443
+
444
+ ```ts
445
+ interface UseGridLayoutOptions {
446
+ /** Initial layout */
447
+ layout: Layout;
448
+ /** Number of columns */
449
+ cols: number;
450
+ /** Compaction type: 'vertical', 'horizontal', or null */
451
+ compactType?: CompactType;
452
+ /** Allow items to overlap (stack on top of each other) */
453
+ allowOverlap?: boolean;
454
+ /** Block movement into occupied space instead of pushing items (no effect if allowOverlap is true) */
455
+ preventCollision?: boolean;
456
+ /** Called when layout changes */
457
+ onLayoutChange?: (layout: Layout) => void;
458
+ }
263
459
 
264
- `WidthProvider` accepts a single prop, `measureBeforeMount`. If `true`, `WidthProvider` will measure the
265
- container's width before mounting children. Use this if you'd like to completely eliminate any resizing animation
266
- on application/component mount.
460
+ interface UseGridLayoutResult {
461
+ /** Current layout */
462
+ layout: Layout;
463
+ /** Set layout directly */
464
+ setLayout: (layout: Layout) => void;
465
+ /** Current drag state (activeDrag, oldDragItem, oldLayout) */
466
+ dragState: DragState;
467
+ /** Current resize state (resizing, oldResizeItem, oldLayout) */
468
+ resizeState: ResizeState;
469
+ /** Current drop state (droppingDOMNode, droppingPosition) */
470
+ dropState: DropState;
471
+ /** Start dragging an item */
472
+ onDragStart: (itemId: string, x: number, y: number) => LayoutItem | null;
473
+ /** Update drag position */
474
+ onDrag: (itemId: string, x: number, y: number) => void;
475
+ /** Stop dragging */
476
+ onDragStop: (itemId: string, x: number, y: number) => void;
477
+ /** Start resizing an item */
478
+ onResizeStart: (itemId: string) => LayoutItem | null;
479
+ /** Update resize dimensions */
480
+ onResize: (
481
+ itemId: string,
482
+ w: number,
483
+ h: number,
484
+ x?: number,
485
+ y?: number
486
+ ) => void;
487
+ /** Stop resizing */
488
+ onResizeStop: (itemId: string, w: number, h: number) => void;
489
+ /** Handle external drag over */
490
+ onDropDragOver: (
491
+ droppingItem: LayoutItem,
492
+ position: DroppingPosition
493
+ ) => void;
494
+ /** Handle external drag leave */
495
+ onDropDragLeave: () => void;
496
+ /** Complete external drop */
497
+ onDrop: (droppingItem: LayoutItem) => void;
498
+ /** Container height in grid rows */
499
+ containerHeight: number;
500
+ /** Whether any drag/resize/drop is active */
501
+ isInteracting: boolean;
502
+ /** The compactor being used */
503
+ compactor: Compactor;
504
+ }
505
+ ```
267
506
 
268
- Have a more complicated layout? `WidthProvider` [is very simple](/lib/components/WidthProvider.jsx) and only
269
- listens to window `'resize'` events. If you need more power and flexibility, try the
270
- [SizeMe React HOC](https://github.com/ctrlplusb/react-sizeme) as an alternative to WidthProvider.
507
+ ### useResponsiveLayout
508
+
509
+ Manages responsive breakpoints and generates layouts for different screen sizes. Use this when building a custom responsive grid.
510
+
511
+ **Why use it instead of the Responsive component?**
512
+
513
+ - Direct access to current breakpoint
514
+ - Control over layout generation for new breakpoints
515
+ - Can update layouts for specific breakpoints
516
+ - Build custom breakpoint UIs
517
+
518
+ ```tsx
519
+ import { useContainerWidth, useResponsiveLayout } from "react-grid-layout";
520
+
521
+ function CustomResponsiveGrid() {
522
+ const { width, containerRef, mounted } = useContainerWidth();
523
+
524
+ const {
525
+ layout, // Current layout for active breakpoint
526
+ layouts, // All layouts by breakpoint
527
+ breakpoint, // Current active breakpoint ('lg', 'md', etc.)
528
+ cols, // Column count for current breakpoint
529
+ setLayoutForBreakpoint,
530
+ setLayouts,
531
+ sortedBreakpoints
532
+ } = useResponsiveLayout({
533
+ width,
534
+ breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 },
535
+ cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
536
+ layouts: {
537
+ lg: [{ i: "1", x: 0, y: 0, w: 2, h: 2 }],
538
+ md: [{ i: "1", x: 0, y: 0, w: 3, h: 2 }]
539
+ },
540
+ compactType: "vertical",
541
+ onBreakpointChange: (bp, cols) =>
542
+ console.log(`Now at ${bp} (${cols} cols)`),
543
+ onLayoutChange: (layout, allLayouts) => saveToServer(allLayouts)
544
+ });
545
+
546
+ // Show current breakpoint in UI
547
+ return (
548
+ <div ref={containerRef}>
549
+ <div>
550
+ Current breakpoint: {breakpoint} ({cols} columns)
551
+ </div>
552
+ {mounted && (
553
+ <GridLayout width={width} cols={cols} layout={layout}>
554
+ {/* children */}
555
+ </GridLayout>
556
+ )}
557
+ </div>
558
+ );
559
+ }
560
+ ```
271
561
 
272
- ### Grid Layout Props
562
+ **Type Definitions:**
563
+
564
+ ```ts
565
+ interface UseResponsiveLayoutOptions<B extends string = DefaultBreakpoints> {
566
+ /** Current container width */
567
+ width: number;
568
+ /** Breakpoint definitions (name → min-width). Default: {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0} */
569
+ breakpoints?: Record<B, number>;
570
+ /** Column counts per breakpoint. Default: {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2} */
571
+ cols?: Record<B, number>;
572
+ /** Layouts for each breakpoint */
573
+ layouts?: Partial<Record<B, Layout>>;
574
+ /** Compaction type */
575
+ compactType?: "vertical" | "horizontal" | null;
576
+ /** Called when breakpoint changes */
577
+ onBreakpointChange?: (newBreakpoint: B, cols: number) => void;
578
+ /** Called when layout changes */
579
+ onLayoutChange?: (layout: Layout, layouts: Record<B, Layout>) => void;
580
+ /** Called when width changes */
581
+ onWidthChange?: (
582
+ width: number,
583
+ margin: [number, number],
584
+ cols: number,
585
+ padding: [number, number] | null
586
+ ) => void;
587
+ }
273
588
 
274
- RGL supports the following properties (see the source for the final word on this):
589
+ interface UseResponsiveLayoutResult<B extends string = DefaultBreakpoints> {
590
+ /** Current layout for the active breakpoint */
591
+ layout: Layout;
592
+ /** All layouts by breakpoint */
593
+ layouts: Partial<Record<B, Layout>>;
594
+ /** Current active breakpoint */
595
+ breakpoint: B;
596
+ /** Column count for the current breakpoint */
597
+ cols: number;
598
+ /** Update layout for a specific breakpoint */
599
+ setLayoutForBreakpoint: (breakpoint: B, layout: Layout) => void;
600
+ /** Update all layouts */
601
+ setLayouts: (layouts: Partial<Record<B, Layout>>) => void;
602
+ /** Sorted array of breakpoint names (smallest to largest) */
603
+ sortedBreakpoints: B[];
604
+ }
275
605
 
276
- ```js
277
- //
278
- // Basic props
279
- //
280
-
281
- // This allows setting the initial width on the server side.
282
- // This is required unless using the HOC <WidthProvider> or similar
283
- width: number,
284
-
285
- // If true, the container height swells and contracts to fit contents
286
- autoSize: ?boolean = true,
287
-
288
- // Number of columns in this layout.
289
- cols: ?number = 12,
290
-
291
- // A CSS selector for tags that will not be draggable.
292
- // For example: draggableCancel:'.MyNonDraggableAreaClassName'
293
- // If you forget the leading . it will not work.
294
- // .react-resizable-handle" is always prepended to this value.
295
- draggableCancel: ?string = '',
296
-
297
- // A CSS selector for tags that will act as the draggable handle.
298
- // For example: draggableHandle:'.MyDragHandleClassName'
299
- // If you forget the leading . it will not work.
300
- draggableHandle: ?string = '',
301
-
302
- // Compaction type.
303
- compactType: ?('vertical' | 'horizontal' | null) = 'vertical';
304
-
305
- // Layout is an array of objects with the format:
306
- // The index into the layout must match the key used on each item component.
307
- // If you choose to use custom keys, you can specify that key in the layout
308
- // array objects using the `i` prop.
309
- layout: ?Array<{i?: string, x: number, y: number, w: number, h: number}> = null, // If not provided, use data-grid props on children
310
-
311
- // Margin between items [x, y] in px.
312
- margin: ?[number, number] = [10, 10],
313
-
314
- // Padding inside the container [x, y] in px
315
- containerPadding: ?[number, number] = margin,
316
-
317
- // Rows have a static height, but you can change this based on breakpoints
318
- // if you like.
319
- rowHeight: ?number = 150,
320
-
321
- // Configuration of a dropping element. Dropping element is a "virtual" element
322
- // which appears when you drag over some element from outside.
323
- // It can be changed by passing specific parameters:
324
- // i - id of an element
325
- // w - width of an element
326
- // h - height of an element
327
- droppingItem?: { i: string, w: number, h: number }
328
-
329
- //
330
- // Flags
331
- //
332
- isDraggable: ?boolean = true,
333
- isResizable: ?boolean = true,
334
- isBounded: ?boolean = false,
335
- // Uses CSS3 translate() instead of position top/left.
336
- // This makes about 6x faster paint performance
337
- useCSSTransforms: ?boolean = true,
338
- // If parent DOM node of ResponsiveReactGridLayout or ReactGridLayout has "transform: scale(n)" css property,
339
- // we should set scale coefficient to avoid render artefacts while dragging.
340
- transformScale: ?number = 1,
341
-
342
- // If true, grid can be placed one over the other.
343
- // If set, implies `preventCollision`.
344
- allowOverlap: ?boolean = false,
345
-
346
- // If true, grid items won't change position when being
347
- // dragged over. If `allowOverlap` is still false,
348
- // this simply won't allow one to drop on an existing object.
349
- preventCollision: ?boolean = false,
350
-
351
- // If true, droppable elements (with `draggable={true}` attribute)
352
- // can be dropped on the grid. It triggers "onDrop" callback
353
- // with position and event object as parameters.
354
- // It can be useful for dropping an element in a specific position
355
- //
356
- // NOTE: In case of using Firefox you should add
357
- // `onDragStart={e => e.dataTransfer.setData('text/plain', '')}` attribute
358
- // along with `draggable={true}` otherwise this feature will work incorrect.
359
- // onDragStart attribute is required for Firefox for a dragging initialization
360
- // @see https://bugzilla.mozilla.org/show_bug.cgi?id=568313
361
- isDroppable: ?boolean = false,
362
- // Defines which resize handles should be rendered.
363
- // Allows for any combination of:
364
- // 's' - South handle (bottom-center)
365
- // 'w' - West handle (left-center)
366
- // 'e' - East handle (right-center)
367
- // 'n' - North handle (top-center)
368
- // 'sw' - Southwest handle (bottom-left)
369
- // 'nw' - Northwest handle (top-left)
370
- // 'se' - Southeast handle (bottom-right)
371
- // 'ne' - Northeast handle (top-right)
372
- //
373
- // Note that changing this property dynamically does not work due to a restriction in react-resizable.
374
- resizeHandles: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se'],
375
- // Custom component for resize handles
376
- // See `handle` as used in https://github.com/react-grid-layout/react-resizable#resize-handle
377
- // Your component should have the class `.react-resizable-handle`, or you should add your custom
378
- // class to the `draggableCancel` prop.
379
- resizeHandle?: ReactElement<any> | ((resizeHandleAxis: ResizeHandleAxis, ref: ReactRef<HTMLElement>) => ReactElement<any>),
380
-
381
- //
382
- // Callbacks
383
- //
384
-
385
- // Callback so you can save the layout.
386
- // Calls back with (currentLayout) after every drag or resize stop.
387
- onLayoutChange: (layout: Layout) => void,
388
-
389
- //
390
- // All callbacks below have signature (layout, oldItem, newItem, placeholder, e, element).
391
- // 'start' and 'stop' callbacks pass `undefined` for 'placeholder'.
392
- //
393
- type ItemCallback = (layout: Layout, oldItem: LayoutItem, newItem: LayoutItem,
394
- placeholder: LayoutItem, e: MouseEvent, element: HTMLElement) => void,
395
-
396
- // Calls when drag starts.
397
- onDragStart: ItemCallback,
398
- // Calls on each drag movement.
399
- onDrag: ItemCallback,
400
- // Calls when drag is complete.
401
- onDragStop: ItemCallback,
402
- // Calls when resize starts.
403
- onResizeStart: ItemCallback,
404
- // Calls when resize movement happens.
405
- onResize: ItemCallback,
406
- // Calls when resize is complete.
407
- onResizeStop: ItemCallback,
408
-
409
- //
410
- // Dropover functionality
411
- //
412
-
413
- // Calls when an element has been dropped into the grid from outside.
414
- onDrop: (layout: Layout, item: ?LayoutItem, e: Event) => void,
415
- // Calls when an element is being dragged over the grid from outside as above.
416
- // This callback should return an object to dynamically change the droppingItem size
417
- // Return false to short-circuit the dragover
418
- onDropDragOver: (e: DragOverEvent) => ?({|w?: number, h?: number|} | false),
419
-
420
- // Ref for getting a reference for the grid's wrapping div.
421
- // You can use this instead of a regular ref and the deprecated `ReactDOM.findDOMNode()`` function.
422
- // Note that this type is React.Ref<HTMLDivElement> in TypeScript, Flow has a bug here
423
- // https://github.com/facebook/flow/issues/8671#issuecomment-862634865
424
- innerRef: {current: null | HTMLDivElement},
425
- ```
426
-
427
- ### Responsive Grid Layout Props
428
-
429
- The responsive grid layout can be used instead. It supports all of the props above, excepting `layout`.
430
- The new properties and changes are:
606
+ type DefaultBreakpoints = "lg" | "md" | "sm" | "xs" | "xxs";
607
+ ```
431
608
 
432
- ```js
433
- // {name: pxVal}, e.g. {lg: 1200, md: 996, sm: 768, xs: 480}
434
- // Breakpoint names are arbitrary but must match in the cols and layouts objects.
435
- breakpoints: ?Object = {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0},
609
+ ## API Reference
610
+
611
+ ### ReactGridLayout Props
612
+
613
+ The v2 API uses composable configuration interfaces for cleaner prop organization:
614
+
615
+ ```ts
616
+ interface ReactGridLayoutProps {
617
+ // Required
618
+ children: React.ReactNode;
619
+ width: number; // Container width in pixels
620
+
621
+ // Configuration interfaces (see below for details)
622
+ gridConfig?: Partial<GridConfig>; // Grid measurement settings
623
+ dragConfig?: Partial<DragConfig>; // Drag behavior settings
624
+ resizeConfig?: Partial<ResizeConfig>; // Resize behavior settings
625
+ dropConfig?: Partial<DropConfig>; // External drop settings
626
+ positionStrategy?: PositionStrategy; // CSS positioning strategy
627
+ compactor?: Compactor; // Layout compaction strategy
628
+
629
+ // Layout data
630
+ layout?: Layout; // Layout definition
631
+ droppingItem?: LayoutItem; // Item configuration when dropping from outside
632
+
633
+ // Container
634
+ autoSize?: boolean; // Auto-size container height (default: true)
635
+ className?: string;
636
+ style?: React.CSSProperties;
637
+ innerRef?: React.Ref<HTMLDivElement>;
638
+
639
+ // Callbacks
640
+ onLayoutChange?: (layout: Layout) => void;
641
+ onDragStart?: EventCallback;
642
+ onDrag?: EventCallback;
643
+ onDragStop?: EventCallback;
644
+ onResizeStart?: EventCallback;
645
+ onResize?: EventCallback;
646
+ onResizeStop?: EventCallback;
647
+ onDrop?: (layout: Layout, item: LayoutItem | undefined, e: Event) => void;
648
+ onDropDragOver?: (e: DragEvent) => { w?: number; h?: number } | false | void;
649
+ }
650
+ ```
436
651
 
437
- // # of cols. This is a breakpoint -> cols map, e.g. {lg: 12, md: 10, ...}
438
- cols: ?Object = {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},
652
+ ### GridConfig
439
653
 
654
+ Grid measurement configuration:
440
655
 
441
- // margin (in pixels). Can be specified either as horizontal and vertical margin, e.g. `[10, 10]` or as a breakpoint -> margin map, e.g. `{lg: [10, 10], md: [10, 10], ...}.
442
- margin: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]},
656
+ ```ts
657
+ interface GridConfig {
658
+ cols: number; // Number of columns (default: 12)
659
+ rowHeight: number; // Row height in pixels (default: 150)
660
+ margin: [number, number]; // [x, y] margin between items (default: [10, 10])
661
+ containerPadding: [number, number] | null; // Container padding (default: null, uses margin)
662
+ maxRows: number; // Maximum rows (default: Infinity)
663
+ }
664
+ ```
443
665
 
666
+ ### DragConfig
444
667
 
445
- // containerPadding (in pixels). Can be specified either as horizontal and vertical padding, e.g. `[10, 10]` or as a breakpoint -> containerPadding map, e.g. `{lg: [10, 10], md: [10, 10], ...}.
446
- containerPadding: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]},
668
+ Drag behavior configuration:
447
669
 
670
+ ```ts
671
+ interface DragConfig {
672
+ enabled: boolean; // Enable dragging (default: true)
673
+ bounded: boolean; // Keep items within container (default: false)
674
+ handle?: string; // CSS selector for drag handle
675
+ cancel?: string; // CSS selector to cancel dragging
676
+ threshold: number; // Pixels to move before drag starts (default: 3)
677
+ }
678
+ ```
448
679
 
449
- // layouts is an object mapping breakpoints to layouts.
450
- // e.g. {lg: Layout, md: Layout, ...}
451
- layouts: {[key: $Keys<breakpoints>]: Layout},
680
+ ### ResizeConfig
452
681
 
453
- //
454
- // Callbacks
455
- //
682
+ Resize behavior configuration:
456
683
 
457
- // Calls back with breakpoint and new # cols
458
- onBreakpointChange: (newBreakpoint: string, newCols: number) => void,
684
+ ```ts
685
+ interface ResizeConfig {
686
+ enabled: boolean; // Enable resizing (default: true)
687
+ handles: ResizeHandleAxis[]; // Handle positions (default: ['se'])
688
+ handleComponent?: React.ReactNode | ((axis, ref) => React.ReactNode);
689
+ }
690
+ ```
459
691
 
460
- // Callback so you can save the layout.
461
- // AllLayouts are keyed by breakpoint.
462
- onLayoutChange: (currentLayout: Layout, allLayouts: {[key: $Keys<breakpoints>]: Layout}) => void,
692
+ ### DropConfig
463
693
 
464
- // Callback when the width changes, so you can modify the layout as needed.
465
- onWidthChange: (containerWidth: number, margin: [number, number], cols: number, containerPadding: [number, number]) => void;
694
+ External drop configuration:
466
695
 
696
+ ```ts
697
+ interface DropConfig {
698
+ enabled: boolean; // Allow external drops (default: false)
699
+ defaultItem: { w: number; h: number }; // Default size (default: { w: 1, h: 1 })
700
+ onDragOver?: (e: DragEvent) => { w?: number; h?: number } | false | void;
701
+ }
467
702
  ```
468
703
 
469
- ### Grid Item Props
704
+ ### PositionStrategy
470
705
 
471
- RGL supports the following properties on grid items or layout items. When initializing a grid,
472
- build a layout array (as in the first example above), or attach this object as the `data-grid` property
473
- to each of your child elements (as in the second example).
706
+ CSS positioning strategy. Built-in options:
474
707
 
475
- If `data-grid` is provided on an item, it will take precedence over an item in the `layout` with the same key (`i`).
708
+ ```ts
709
+ import {
710
+ transformStrategy, // Default: use CSS transforms
711
+ absoluteStrategy, // Use top/left positioning
712
+ createScaledStrategy // For scaled containers
713
+ } from "react-grid-layout/core";
476
714
 
477
- Note that if a grid item is provided but incomplete (missing one of `x, y, w, or h`), an error
478
- will be thrown so you can correct your layout.
715
+ // Example: scaled container
716
+ <div style={{ transform: 'scale(0.5)' }}>
717
+ <ReactGridLayout positionStrategy={createScaledStrategy(0.5)} ... />
718
+ </div>
719
+ ```
479
720
 
480
- If no properties are provided for a grid item, one will be generated with a width and height of `1`.
721
+ ### Compactor
481
722
 
482
- You can set minimums and maximums for each dimension. This is for resizing; it of course has no effect if resizing
483
- is disabled. Errors will be thrown if your mins and maxes overlap incorrectly, or your initial dimensions
484
- are out of range.
723
+ Layout compaction strategy. Built-in options:
485
724
 
486
- Any `<GridItem>` properties defined directly will take precedence over globally-set options. For
487
- example, if the layout has the property `isDraggable: false`, but the grid item has the prop `isDraggable: true`, the item
488
- will be draggable, even if the item is marked `static: true`.
725
+ ```ts
726
+ import {
727
+ verticalCompactor, // Default: compact items upward
728
+ horizontalCompactor, // Compact items leftward
729
+ noCompactor, // No compaction (free positioning)
730
+ getCompactor // Factory: getCompactor('vertical', allowOverlap, preventCollision)
731
+ } from "react-grid-layout/core";
732
+ ```
489
733
 
490
- ```js
491
- {
492
-
493
- // A string corresponding to the component key
494
- i: string,
495
-
496
- // These are all in grid units, not pixels
497
- x: number,
498
- y: number,
499
- w: number,
500
- h: number,
501
- minW: ?number = 0,
502
- maxW: ?number = Infinity,
503
- minH: ?number = 0,
504
- maxH: ?number = Infinity,
505
-
506
- // If true, equal to `isDraggable: false, isResizable: false`.
507
- static: ?boolean = false,
508
- // If false, will not be draggable. Overrides `static`.
509
- isDraggable: ?boolean = true,
510
- // If false, will not be resizable. Overrides `static`.
511
- isResizable: ?boolean = true,
512
- // By default, a handle is only shown on the bottom-right (southeast) corner.
513
- // As of RGL >= 1.4.0, resizing on any corner works just fine!
514
- resizeHandles?: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se']
515
- // If true and draggable, item will be moved only within grid.
516
- isBounded: ?boolean = false
734
+ ### ResponsiveGridLayout Props
735
+
736
+ Extends `GridLayoutProps` with responsive-specific props:
737
+
738
+ ```ts
739
+ interface ResponsiveGridLayoutProps<B extends string = string> {
740
+ // Responsive configuration
741
+ breakpoint?: B; // Current breakpoint (auto-detected)
742
+ breakpoints?: Record<B, number>; // Breakpoint definitions (default: {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0})
743
+ cols?: Record<B, number>; // Columns per breakpoint (default: {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2})
744
+ layouts?: Record<B, Layout>; // Layouts per breakpoint
745
+
746
+ // Can be fixed or per-breakpoint
747
+ margin?: [number, number] | Partial<Record<B, [number, number]>>;
748
+ containerPadding?:
749
+ | [number, number]
750
+ | Partial<Record<B, [number, number] | null>>
751
+ | null;
752
+
753
+ // Callbacks
754
+ onBreakpointChange?: (newBreakpoint: B, cols: number) => void;
755
+ onLayoutChange?: (layout: Layout, layouts: Record<B, Layout>) => void;
756
+ onWidthChange?: (
757
+ width: number,
758
+ margin: [number, number],
759
+ cols: number,
760
+ padding: [number, number] | null
761
+ ) => void;
517
762
  }
518
763
  ```
519
764
 
520
- ### Grid Item Heights and Widths
765
+ ### Layout Item
766
+
767
+ ```ts
768
+ interface LayoutItem {
769
+ i: string; // Unique identifier (must match child key)
770
+ x: number; // X position in grid units
771
+ y: number; // Y position in grid units
772
+ w: number; // Width in grid units
773
+ h: number; // Height in grid units
774
+ minW?: number; // Minimum width (default: 0)
775
+ maxW?: number; // Maximum width (default: Infinity)
776
+ minH?: number; // Minimum height (default: 0)
777
+ maxH?: number; // Maximum height (default: Infinity)
778
+ static?: boolean; // If true, not draggable or resizable
779
+ isDraggable?: boolean; // Override grid isDraggable
780
+ isResizable?: boolean; // Override grid isResizable
781
+ isBounded?: boolean; // Override grid isBounded
782
+ resizeHandles?: Array<"s" | "w" | "e" | "n" | "sw" | "nw" | "se" | "ne">;
783
+ }
784
+ ```
521
785
 
522
- Grid item widths are based on container and number of columns. The size of a grid unit's height is based on `rowHeight`.
786
+ ### Core Utilities
523
787
 
524
- Note that an item that has `h=2` is _not exactly twice as tall as one with `h=1` unless you have no `margin`_!
788
+ Import pure layout functions from `react-grid-layout/core`:
525
789
 
526
- In order for the grid to not be ragged, when an item spans grid units, it must also span margins. So you must add the height or width or the margin you are spanning for each unit. So actual pixel height is `(rowHeight * h) + (marginH * (h - 1)`.
790
+ ```ts
791
+ import {
792
+ compact,
793
+ moveElement,
794
+ collides,
795
+ getFirstCollision,
796
+ validateLayout
797
+ // ... and more
798
+ } from "react-grid-layout/core";
799
+ ```
527
800
 
528
- For example, with `rowHeight=30`, `margin=[10,10]` and a unit with height 4, the calculation is `(30 * 4) + (10 * 3)`
801
+ ## Extending: Custom Compactors & Position Strategies
802
+
803
+ ### Creating a Custom Compactor
804
+
805
+ Compactors control how items are arranged after drag/resize. Create your own for custom layouts like masonry, gravity, or shelf-packing.
806
+
807
+ **The Compactor Interface:**
808
+
809
+ ```ts
810
+ interface Compactor {
811
+ /** Identifies the compaction type */
812
+ type: "vertical" | "horizontal" | null | string;
813
+
814
+ /**
815
+ * Whether items can overlap each other.
816
+ *
817
+ * When true:
818
+ * - Items can be placed on top of other items
819
+ * - Dragging into another item does NOT push it away
820
+ * - Compaction is skipped after drag/resize
821
+ *
822
+ * Use for: layered dashboards, free-form layouts
823
+ */
824
+ allowOverlap: boolean;
825
+
826
+ /**
827
+ * Whether to block movement that would cause collision.
828
+ *
829
+ * When true (and allowOverlap is false):
830
+ * - Dragging into another item is blocked (item snaps back)
831
+ * - Other items are NOT pushed away
832
+ * - Only affects movement, not compaction
833
+ *
834
+ * Use with noCompactor for: fixed grids, slot-based layouts
835
+ *
836
+ * Note: Has no effect when allowOverlap is true.
837
+ */
838
+ preventCollision?: boolean;
839
+
840
+ /**
841
+ * Compact the entire layout.
842
+ * Called after any layout change to fill gaps.
843
+ *
844
+ * @param layout - Array of layout items (clone before mutating!)
845
+ * @param cols - Number of grid columns
846
+ * @returns New compacted layout
847
+ */
848
+ compact(layout: Layout, cols: number): Layout;
849
+
850
+ /**
851
+ * Handle moving an item.
852
+ * Called during drag to preview the new position.
853
+ *
854
+ * @param layout - Current layout
855
+ * @param item - Item being moved
856
+ * @param x - New X position in grid units
857
+ * @param y - New Y position in grid units
858
+ * @param cols - Number of grid columns
859
+ * @returns Updated layout with item at new position
860
+ */
861
+ onMove(
862
+ layout: Layout,
863
+ item: LayoutItem,
864
+ x: number,
865
+ y: number,
866
+ cols: number
867
+ ): Layout;
868
+ }
869
+ ```
529
870
 
530
- ![margin](margin.png)
871
+ **Example: Gravity Compactor (items fall to bottom)**
872
+
873
+ ```ts
874
+ import { cloneLayout, cloneLayoutItem, getStatics, bottom } from "react-grid-layout/core";
875
+
876
+ const gravityCompactor: Compactor = {
877
+ type: "gravity",
878
+ allowOverlap: false,
879
+
880
+ compact(layout, cols) {
881
+ const statics = getStatics(layout);
882
+ const maxY = 100; // arbitrary max height
883
+ const out = [];
884
+
885
+ // Sort by Y descending (process bottom items first)
886
+ const sorted = [...layout].sort((a, b) => b.y - a.y);
887
+
888
+ for (const item of sorted) {
889
+ const l = cloneLayoutItem(item);
890
+
891
+ if (!l.static) {
892
+ // Move down as far as possible
893
+ while (l.y < maxY && !collides(l, statics)) {
894
+ l.y++;
895
+ }
896
+ l.y--; // Back up one
897
+ }
898
+
899
+ out.push(l);
900
+ }
901
+
902
+ return out;
903
+ },
904
+
905
+ onMove(layout, item, x, y, cols) {
906
+ const newLayout = cloneLayout(layout);
907
+ const movedItem = newLayout.find(l => l.i === item.i);
908
+ if (movedItem) {
909
+ movedItem.x = x;
910
+ movedItem.y = y;
911
+ movedItem.moved = true;
912
+ }
913
+ return newLayout;
914
+ }
915
+ };
531
916
 
532
- If this is a problem for you, set `margin=[0,0]` and handle visual spacing between your elements inside the elements' content.
917
+ // Usage
918
+ <GridLayout compactor={gravityCompactor} />
919
+ ```
533
920
 
534
- ### Performance
921
+ **Example: Single Row Compactor (horizontal shelf)**
922
+
923
+ ```ts
924
+ const singleRowCompactor: Compactor = {
925
+ type: "shelf",
926
+ allowOverlap: false,
927
+
928
+ compact(layout, cols) {
929
+ let x = 0;
930
+ const out = [];
931
+
932
+ // Sort by original X position
933
+ const sorted = [...layout].sort((a, b) => a.x - b.x);
934
+
935
+ for (const item of sorted) {
936
+ const l = cloneLayoutItem(item);
937
+ if (!l.static) {
938
+ l.x = x;
939
+ l.y = 0; // All items on row 0
940
+ x += l.w;
941
+
942
+ // Wrap to next row if overflow
943
+ if (x > cols) {
944
+ l.x = 0;
945
+ x = l.w;
946
+ }
947
+ }
948
+ out.push(l);
949
+ }
950
+
951
+ return out;
952
+ },
953
+
954
+ onMove(layout, item, x, y, cols) {
955
+ // Same as default - just update position
956
+ const newLayout = cloneLayout(layout);
957
+ const movedItem = newLayout.find(l => l.i === item.i);
958
+ if (movedItem) {
959
+ movedItem.x = x;
960
+ movedItem.y = 0; // Force row 0
961
+ movedItem.moved = true;
962
+ }
963
+ return newLayout;
964
+ }
965
+ };
966
+ ```
535
967
 
536
- `<ReactGridLayout>` has [an optimized `shouldComponentUpdate` implementation](lib/ReactGridLayout.jsx), but it relies on the user memoizing the `children` array:
968
+ **Using Helper Functions:**
969
+
970
+ The core module exports helpers for building compactors:
971
+
972
+ ```ts
973
+ import {
974
+ resolveCompactionCollision, // Move items to resolve overlaps
975
+ compactItemVertical, // Compact one item upward
976
+ compactItemHorizontal, // Compact one item leftward
977
+ getFirstCollision, // Find first collision
978
+ collides, // Check if two items collide
979
+ getStatics, // Get static items from layout
980
+ cloneLayout, // Clone layout array
981
+ cloneLayoutItem // Clone single item
982
+ } from "react-grid-layout/core";
983
+ ```
984
+
985
+ ### Creating a Custom Position Strategy
986
+
987
+ Position strategies control how items are positioned via CSS. Create custom strategies for special transform handling.
988
+
989
+ **The PositionStrategy Interface:**
990
+
991
+ ```ts
992
+ interface PositionStrategy {
993
+ /** Type identifier */
994
+ type: "transform" | "absolute" | string;
995
+
996
+ /** Scale factor for coordinate calculations */
997
+ scale: number;
998
+
999
+ /**
1000
+ * Generate CSS styles for positioning an item.
1001
+ *
1002
+ * @param pos - Position with top, left, width, height in pixels
1003
+ * @returns CSS properties object
1004
+ */
1005
+ calcStyle(pos: Position): React.CSSProperties;
1006
+
1007
+ /**
1008
+ * Calculate drag position from mouse coordinates.
1009
+ * Used during drag to convert screen coords to grid coords.
1010
+ *
1011
+ * @param clientX - Mouse X position
1012
+ * @param clientY - Mouse Y position
1013
+ * @param offsetX - Offset from item left edge
1014
+ * @param offsetY - Offset from item top edge
1015
+ * @returns Calculated left/top position
1016
+ */
1017
+ calcDragPosition(
1018
+ clientX: number,
1019
+ clientY: number,
1020
+ offsetX: number,
1021
+ offsetY: number
1022
+ ): { left: number; top: number };
1023
+ }
1024
+ ```
1025
+
1026
+ **Example: Rotated Container Strategy**
1027
+
1028
+ ```ts
1029
+ const createRotatedStrategy = (angleDegrees: number): PositionStrategy => {
1030
+ const angleRad = (angleDegrees * Math.PI) / 180;
1031
+ const cos = Math.cos(angleRad);
1032
+ const sin = Math.sin(angleRad);
1033
+
1034
+ return {
1035
+ type: "rotated",
1036
+ scale: 1,
1037
+
1038
+ calcStyle(pos) {
1039
+ // Apply rotation to position
1040
+ const rotatedX = pos.left * cos - pos.top * sin;
1041
+ const rotatedY = pos.left * sin + pos.top * cos;
1042
+
1043
+ return {
1044
+ transform: `translate(${rotatedX}px, ${rotatedY}px)`,
1045
+ width: `${pos.width}px`,
1046
+ height: `${pos.height}px`,
1047
+ position: "absolute"
1048
+ };
1049
+ },
1050
+
1051
+ calcDragPosition(clientX, clientY, offsetX, offsetY) {
1052
+ // Reverse the rotation for drag calculations
1053
+ const x = clientX - offsetX;
1054
+ const y = clientY - offsetY;
1055
+
1056
+ return {
1057
+ left: x * cos + y * sin,
1058
+ top: -x * sin + y * cos
1059
+ };
1060
+ }
1061
+ };
1062
+ };
1063
+
1064
+ // Usage: grid inside a rotated container
1065
+ <div style={{ transform: 'rotate(45deg)' }}>
1066
+ <GridLayout positionStrategy={createRotatedStrategy(45)} />
1067
+ </div>
1068
+ ```
1069
+
1070
+ **Example: 3D Perspective Strategy**
1071
+
1072
+ ```ts
1073
+ const create3DStrategy = (
1074
+ perspective: number,
1075
+ rotateX: number
1076
+ ): PositionStrategy => ({
1077
+ type: "3d",
1078
+ scale: 1,
1079
+
1080
+ calcStyle(pos) {
1081
+ return {
1082
+ transform: `
1083
+ perspective(${perspective}px)
1084
+ rotateX(${rotateX}deg)
1085
+ translate3d(${pos.left}px, ${pos.top}px, 0)
1086
+ `,
1087
+ width: `${pos.width}px`,
1088
+ height: `${pos.height}px`,
1089
+ position: "absolute",
1090
+ transformStyle: "preserve-3d"
1091
+ };
1092
+ },
1093
+
1094
+ calcDragPosition(clientX, clientY, offsetX, offsetY) {
1095
+ // Adjust for perspective foreshortening
1096
+ const perspectiveFactor = 1 + clientY / perspective;
1097
+ return {
1098
+ left: (clientX - offsetX) / perspectiveFactor,
1099
+ top: (clientY - offsetY) / perspectiveFactor
1100
+ };
1101
+ }
1102
+ });
1103
+ ```
1104
+
1105
+ ## Extras
1106
+
1107
+ 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.
1108
+
1109
+ ### GridBackground
1110
+
1111
+ Renders an SVG grid background that aligns with GridLayout cells. Use this to visualize the grid structure behind your layout.
1112
+
1113
+ > Based on [PR #2175](https://github.com/react-grid-layout/react-grid-layout/pull/2175) by [@dmj900501](https://github.com/dmj900501).
1114
+
1115
+ ```tsx
1116
+ import { GridBackground } from "react-grid-layout/extras";
1117
+ import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
1118
+
1119
+ function MyGrid() {
1120
+ const { width, containerRef, mounted } = useContainerWidth();
537
1121
 
538
- ```js
539
- // lib/ReactGridLayout.jsx
540
- // ...
541
- shouldComponentUpdate(nextProps: Props, nextState: State) {
542
1122
  return (
543
- // NOTE: this is almost always unequal. Therefore the only way to get better performance
544
- // from SCU is if the user intentionally memoizes children. If they do, and they can
545
- // handle changes properly, performance will increase.
546
- this.props.children !== nextProps.children ||
547
- !fastRGLPropsEqual(this.props, nextProps, isEqual) ||
548
- !isEqual(this.state.activeDrag, nextState.activeDrag)
1123
+ <div ref={containerRef} style={{ position: "relative" }}>
1124
+ {mounted && (
1125
+ <>
1126
+ <GridBackground
1127
+ width={width}
1128
+ cols={12}
1129
+ rowHeight={30}
1130
+ margin={[10, 10]}
1131
+ rows={10}
1132
+ color="#f0f0f0"
1133
+ borderRadius={4}
1134
+ />
1135
+ <ReactGridLayout
1136
+ width={width}
1137
+ gridConfig={{ cols: 12, rowHeight: 30, margin: [10, 10] }}
1138
+ >
1139
+ {children}
1140
+ </ReactGridLayout>
1141
+ </>
1142
+ )}
1143
+ </div>
549
1144
  );
550
1145
  }
551
- // ...
552
1146
  ```
553
1147
 
554
- If you memoize your children, you can take advantage of this, and reap faster rerenders. For example:
555
-
556
- ```js
557
- function MyGrid(props) {
558
- const children = React.useMemo(() => {
559
- return new Array(props.count).fill(undefined).map((val, idx) => {
560
- return <div key={idx} data-grid={{ x: idx, y: 1, w: 1, h: 1 }} />;
561
- });
562
- }, [props.count]);
563
- return <ReactGridLayout cols={12}>{children}</ReactGridLayout>;
1148
+ **Props:**
1149
+
1150
+ ```ts
1151
+ interface GridBackgroundProps {
1152
+ // Required - must match your GridLayout config
1153
+ width: number; // Container width
1154
+ cols: number; // Number of columns
1155
+ rowHeight: number; // Row height in pixels
1156
+
1157
+ // Optional
1158
+ margin?: [number, number]; // Gap between cells (default: [10, 10])
1159
+ containerPadding?: [number, number] | null; // Container padding (default: uses margin)
1160
+ rows?: number | "auto"; // Number of rows to display (default: 10)
1161
+ height?: number; // Used when rows="auto" to calculate row count
1162
+ color?: string; // Cell background color (default: "#e0e0e0")
1163
+ borderRadius?: number; // Cell border radius (default: 4)
1164
+ className?: string; // Additional CSS class
1165
+ style?: React.CSSProperties; // Additional inline styles
564
1166
  }
565
1167
  ```
566
1168
 
567
- Because the `children` prop doesn't change between rerenders, updates to `<MyGrid>` won't result in new renders, improving performance.
1169
+ ### Fast Compactors
568
1170
 
569
- ### React Hooks Performance
1171
+ 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.
570
1172
 
571
- Using hooks to save your layout state on change will cause the layouts to re-render as the ResponsiveGridLayout will change it's value on every render.
572
- To avoid this you should wrap your WidthProvider in a useMemo:
1173
+ > 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).
573
1174
 
574
- ```js
575
- const ResponsiveReactGridLayout = useMemo(() => WidthProvider(Responsive), []);
1175
+ ```tsx
1176
+ import {
1177
+ fastVerticalCompactor,
1178
+ fastHorizontalCompactor,
1179
+ fastVerticalOverlapCompactor,
1180
+ fastHorizontalOverlapCompactor
1181
+ } from "react-grid-layout/extras";
1182
+
1183
+ <ReactGridLayout
1184
+ compactor={fastVerticalCompactor}
1185
+ // or compactor={fastHorizontalCompactor}
1186
+ layout={layout}
1187
+ width={width}
1188
+ />;
576
1189
  ```
577
1190
 
578
- ### Custom Child Components and Draggable Handles
1191
+ **Performance Benchmarks:**
1192
+
1193
+ | Items | Standard Vertical | Fast Vertical | Speedup |
1194
+ | ----- | ----------------- | ------------- | ------- |
1195
+ | 50 | 112 µs | 19 µs | **6x** |
1196
+ | 100 | 203 µs | 36 µs | **6x** |
1197
+ | 200 | 821 µs | 51 µs | **16x** |
1198
+ | 500 | 5.7 ms | 129 µs | **45x** |
1199
+
1200
+ | Items | Standard Horizontal | Fast Horizontal | Speedup |
1201
+ | ----- | ------------------- | --------------- | ------- |
1202
+ | 50 | 164 µs | 12 µs | **14x** |
1203
+ | 100 | 477 µs | 25 µs | **19x** |
1204
+ | 200 | 1.1 ms | 42 µs | **26x** |
1205
+ | 500 | 9.5 ms | 128 µs | **74x** |
1206
+
1207
+ **Correctness:**
1208
+
1209
+ The fast compactors produce layouts identical to the standard compactors:
1210
+
1211
+ - **Vertical**: 0% height difference on deterministic 100-item layouts
1212
+ - **Horizontal**: 0% width difference on deterministic 100-item layouts
1213
+ - Both pass all correctness tests: no overlaps, idempotent, static item handling
1214
+
1215
+ **When to use:**
1216
+
1217
+ - Use fast compactors for dashboards with 200+ widgets
1218
+ - For smaller layouts (<100 items), standard compactors work equally well
1219
+ - Both standard and fast compactors produce valid, non-overlapping layouts
1220
+
1221
+ ### calcGridCellDimensions (Core Utility)
1222
+
1223
+ For building custom grid overlays or backgrounds, use the `calcGridCellDimensions` utility from `react-grid-layout/core`:
1224
+
1225
+ ```ts
1226
+ import { calcGridCellDimensions } from "react-grid-layout/core";
1227
+
1228
+ const dims = calcGridCellDimensions({
1229
+ width: 1200,
1230
+ cols: 12,
1231
+ rowHeight: 30,
1232
+ margin: [10, 10],
1233
+ containerPadding: [20, 20]
1234
+ });
1235
+
1236
+ // dims = {
1237
+ // cellWidth: 88.33, // Width of each cell
1238
+ // cellHeight: 30, // Height of each cell (= rowHeight)
1239
+ // offsetX: 20, // Left padding
1240
+ // offsetY: 20, // Top padding
1241
+ // gapX: 10, // Horizontal gap between cells
1242
+ // gapY: 10, // Vertical gap between cells
1243
+ // cols: 12, // Column count
1244
+ // containerWidth: 1200
1245
+ // }
1246
+ ```
579
1247
 
580
- If you use React Components as grid children, they need to do a few things:
1248
+ This is useful for building custom visualizations, snap-to-grid functionality, or integrating with canvas/WebGL renderers.
581
1249
 
582
- 1. Forward refs to an underlying DOM node, and
583
- 2. Forward `style`,`className`, `onMouseDown`, `onMouseUp` and `onTouchEnd` to that same DOM node.
1250
+ ## Performance
584
1251
 
585
- For example:
1252
+ ### Memoize Children
1253
+
1254
+ The grid compares children by reference. Memoize them for better performance:
1255
+
1256
+ ```tsx
1257
+ function MyGrid({ count, width }) {
1258
+ const children = useMemo(() => {
1259
+ return Array.from({ length: count }, (_, i) => (
1260
+ <div
1261
+ key={i}
1262
+ data-grid={{ x: i % 12, y: Math.floor(i / 12), w: 1, h: 1 }}
1263
+ />
1264
+ ));
1265
+ }, [count]);
586
1266
 
587
- ```js
588
- const CustomGridItemComponent = React.forwardRef(({style, className, onMouseDown, onMouseUp, onTouchEnd, children, ...props}, ref) => {
589
1267
  return (
590
- <div style={{ /* styles */, ...style}} className={className} ref={ref} onMouseDown={onMouseDown} onMouseUp={onMouseUp} onTouchEnd={onTouchEnd}>
591
- {/* Some other content */}
592
- {children} {/* Make sure to include children to add resizable handle */}
593
- </div>
1268
+ <ReactGridLayout width={width} gridConfig={{ cols: 12 }}>
1269
+ {children}
1270
+ </ReactGridLayout>
594
1271
  );
595
- })
1272
+ }
596
1273
  ```
597
1274
 
598
- The same is true of custom elements as draggable handles using the `draggableHandle` prop. This is so that
599
- the underlying `react-draggable` library can get a reference to the DOM node underneath, manipulate
600
- positioning via `style`, and set classes.
1275
+ ### Avoid Creating Components in Render (Legacy WidthProvider)
1276
+
1277
+ If using the legacy WidthProvider HOC, don't create the component during render:
1278
+
1279
+ ```tsx
1280
+ import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";
1281
+
1282
+ // Bad - creates new component every render
1283
+ function MyGrid() {
1284
+ const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
1285
+ return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
1286
+ }
1287
+
1288
+ // Good - create once outside or with useMemo
1289
+ const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
1290
+
1291
+ function MyGrid() {
1292
+ return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
1293
+ }
1294
+ ```
1295
+
1296
+ With the v2 API, use `useContainerWidth` hook instead to avoid this issue entirely.
1297
+
1298
+ ## Custom Child Components
1299
+
1300
+ Grid children must forward refs and certain props:
1301
+
1302
+ ```tsx
1303
+ const CustomItem = forwardRef<HTMLDivElement, CustomItemProps>(
1304
+ (
1305
+ {
1306
+ style,
1307
+ className,
1308
+ onMouseDown,
1309
+ onMouseUp,
1310
+ onTouchEnd,
1311
+ children,
1312
+ ...props
1313
+ },
1314
+ ref
1315
+ ) => {
1316
+ return (
1317
+ <div
1318
+ ref={ref}
1319
+ style={style}
1320
+ className={className}
1321
+ onMouseDown={onMouseDown}
1322
+ onMouseUp={onMouseUp}
1323
+ onTouchEnd={onTouchEnd}
1324
+ >
1325
+ {children}
1326
+ </div>
1327
+ );
1328
+ }
1329
+ );
1330
+ ```
601
1331
 
602
1332
  ## Contribute
603
1333
 
@@ -605,19 +1335,3 @@ If you have a feature request, please add it as an issue or make a pull request.
605
1335
 
606
1336
  If you have a bug to report, please reproduce the bug in [CodeSandbox](https://codesandbox.io/s/staging-bush-3lvt7?file=/src/ShowcaseLayout.js) to help
607
1337
  us easily isolate it.
608
-
609
- ## TODO List
610
-
611
- - [x] Basic grid layout
612
- - [x] Fluid grid layout
613
- - [x] Grid packing
614
- - [x] Draggable grid items
615
- - [x] Live grid packing while dragging
616
- - [x] Resizable grid items
617
- - [x] Layouts per responsive breakpoint
618
- - [x] Define grid attributes on children themselves (`data-grid` key)
619
- - [x] Static elements
620
- - [x] Persistent id per item for predictable localstorage restores, even when # items changes
621
- - [x] Min/max w/h per item
622
- - [x] Resizable handles on other corners
623
- - [ ] Configurable w/h per breakpoint