React-Grid-Layout
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.
GIF from production usage on BitMEX.com
[Demo | Changelog | CodeSandbox Editable demo]
Table of Contents
Demos
- Showcase
- Basic
- No Dragging/Resizing (Layout Only)
- Messy Layout Autocorrect
- Layout Defined on Children
- Static Elements
- Adding/Removing Elements
- Saving Layout to LocalStorage
- Saving a Responsive Layout to LocalStorage
- Minimum and Maximum Width/Height
- Dynamic Minimum and Maximum Width/Height
- No Vertical Compacting (Free Movement)
- Prevent Collision
- Error Case
- Toolbox
- Drag From Outside
- Bounded Layout
- Resizable Handles
- Scaled Containers
- Allow Overlap
Projects Using React-Grid-Layout
Know of others? Create a PR to let me know!
Features
- 100% React - no jQuery
- 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
- Performance with CSS Transforms: on / off, note paint (green) as % of time
- Compatibility with
<React.StrictMode>
Version | Compatibility |
---|
>= 0.17.0 | React 16 & 17 |
>= 0.11.3 | React 0.14 & 15 |
>= 0.10.0 | React 0.14 |
0.8. - 0.9.2 | React 0.13 |
< 0.8 | React 0.12 |
Installation
Install the React-Grid-Layout package package using npm:
npm install react-grid-layout
Include the following stylesheets in your application:
/node_modules/react-grid-layout/css/styles.css
/node_modules/react-resizable/css/styles.css
Usage
Use ReactGridLayout like any other component. The following example below will
produce a grid with three items where:
- users will not be able to drag or resize item
a
- item
b
will be restricted to a minimum width of 2 grid blocks and a maximum width of 4 grid blocks - users will be able to freely drag and resize item
c
import GridLayout from "react-grid-layout";
class MyFirstGrid extends React.Component {
render() {
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 (
<GridLayout
className="layout"
layout={layout}
cols={12}
rowHeight={30}
width={1200}
>
<div key="a">a</div>
<div key="b">b</div>
<div key="c">c</div>
</GridLayout>
);
}
}
You may also choose to set layout properties directly on the children:
import GridLayout from "react-grid-layout";
class MyFirstGrid extends React.Component {
render() {
return (
<GridLayout className="layout" cols={12} rowHeight={30} width={1200}>
<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, minW: 2, maxW: 4 }}>
b
</div>
<div key="c" data-grid={{ x: 4, y: 0, w: 1, h: 2 }}>
c
</div>
</GridLayout>
);
}
}
Usage without Browserify/Webpack
A module usable in a <script>
tag is included here. It uses a UMD shim and
excludes React
, so it must be otherwise available in your application, either via RequireJS or on window.React
.
Responsive Usage
To make RGL responsive, use the <ResponsiveReactGridLayout>
element:
import { Responsive as ResponsiveGridLayout } from "react-grid-layout";
class MyResponsiveGrid extends React.Component {
render() {
const layouts = getLayoutsFromSomewhere();
return (
<ResponsiveGridLayout
className="layout"
layouts={layouts}
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
>
<div key="1">1</div>
<div key="2">2</div>
<div key="3">3</div>
</ResponsiveGridLayout>
);
}
}
When in responsive mode, you should supply at least one breakpoint via the layouts
property.
When using layouts
, it is best to supply as many breakpoints as possible, especially the largest one.
If the largest is provided, RGL will attempt to interpolate the rest.
You will also need to provide a width
, when using <ResponsiveReactGridLayout>
it is suggested you use the HOC
WidthProvider
as per the instructions below.
It is possible to supply default mappings via the data-grid
property on individual
items, so that they would be taken into account within layout interpolation.
Providing Grid Width
Both <ResponsiveReactGridLayout>
and <ReactGridLayout>
take width
to calculate
positions on drag events. In simple cases a HOC WidthProvider
can be used to automatically determine
width upon initialization and window resize events.
import { Responsive, WidthProvider } from "react-grid-layout";
const ResponsiveGridLayout = WidthProvider(Responsive);
class MyResponsiveGrid extends React.Component {
render() {
var layouts = getLayoutsFromSomewhere();
return (
<ResponsiveGridLayout
className="layout"
layouts={layouts}
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
>
<div key="1">1</div>
<div key="2">2</div>
<div key="3">3</div>
</ResponsiveGridLayout>
);
}
}
This allows you to easily replace WidthProvider
with your own Provider HOC if you need more sophisticated logic.
WidthProvider
accepts a single prop, measureBeforeMount
. If true
, WidthProvider
will measure the
container's width before mounting children. Use this if you'd like to completely eliminate any resizing animation
on application/component mount.
Have a more complicated layout? WidthProvider
is very simple and only
listens to window 'resize'
events. If you need more power and flexibility, try the
SizeMe React HOC as an alternative to WidthProvider.
Grid Layout Props
RGL supports the following properties (see the source for the final word on this):
width: number,
autoSize: ?boolean = true,
cols: ?number = 12,
draggableCancel: ?string = '',
draggableHandle: ?string = '',
compactType: ?('vertical' | 'horizontal') = 'vertical';
layout: ?array = null,
margin: ?[number, number] = [10, 10],
containerPadding: ?[number, number] = margin,
rowHeight: ?number = 150,
droppingItem?: { i: string, w: number, h: number }
isDraggable: ?boolean = true,
isResizable: ?boolean = true,
isBounded: ?boolean = false,
useCSSTransforms: ?boolean = true,
transformScale: ?number = 1,
allowOverlap: ?boolean = false,
preventCollision: ?boolean = false,
isDroppable: ?boolean = false,
resizeHandles: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se'],
resizeHandle?: ReactElement<any> | ((resizeHandleAxis: ResizeHandleAxis, ref: ReactRef<HTMLElement>) => ReactElement<any>),
onLayoutChange: (layout: Layout) => void,
type ItemCallback = (layout: Layout, oldItem: LayoutItem, newItem: LayoutItem,
placeholder: LayoutItem, e: MouseEvent, element: HTMLElement) => void,
onDragStart: ItemCallback,
onDrag: ItemCallback,
onDragStop: ItemCallback,
onResizeStart: ItemCallback,
onResize: ItemCallback,
onResizeStop: ItemCallback,
onDrop: (layout: Layout, item: ?LayoutItem, e: Event) => void,
onDropDragOver: (e: DragOverEvent) => ?({|w?: number, h?: number|} | false),
innerRef: {current: null | HTMLDivElement},
Responsive Grid Layout Props
The responsive grid layout can be used instead. It supports all of the props above, excepting layout
.
The new properties and changes are:
breakpoints: ?Object = {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0},
cols: ?Object = {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},
margin: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]},
containerPadding: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]},
layouts: {[key: $Keys<breakpoints>]: Layout},
onBreakpointChange: (newBreakpoint: string, newCols: number) => void,
onLayoutChange: (currentLayout: Layout, allLayouts: {[key: $Keys<breakpoints>]: Layout}) => void,
onWidthChange: (containerWidth: number, margin: [number, number], cols: number, containerPadding: [number, number]) => void;
Grid Item Props
RGL supports the following properties on grid items or layout items. When initializing a grid,
build a layout array (as in the first example above), or attach this object as the data-grid
property
to each of your child elements (as in the second example).
Note that if a grid item is provided but incomplete (missing one of x, y, w, or h
), an error
will be thrown so you can correct your layout.
If no properties are provided for a grid item, one will be generated with a width and height of 1
.
You can set minimums and maximums for each dimension. This is for resizing; it of course has no effect if resizing
is disabled. Errors will be thrown if your mins and maxes overlap incorrectly, or your initial dimensions
are out of range.
Any <GridItem>
properties defined directly will take precedence over globally-set options. For
example, if the layout has the property isDraggable: false
, but the grid item has the prop isDraggable: true
, the item
will be draggable, even if the item is marked static: true
.
{
i: string,
x: number,
y: number,
w: number,
h: number,
minW: ?number = 0,
maxW: ?number = Infinity,
minH: ?number = 0,
maxH: ?number = Infinity,
static: ?boolean = false,
isDraggable: ?boolean = true,
isResizable: ?boolean = true,
resizeHandles?: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se']
isBounded: ?boolean = false
}
Grid Item Heights and Widths
Grid item widths are based on container and number of columns. The size of a grid unit's height is based on rowHeight
.
Note that an item that has h=2
is not exactly twice as tall as one with h=1
unless you have no margin
!
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)
.
For example, with rowHeight=30
, margin=[10,10]
and a unit with height 4, the calculation is (30 * 4) + (10 * 3)
If this is a problem for you, set margin=[0,0]
and handle visual spacing between your elements inside the elements' content.
Performance
<ReactGridLayout>
has an optimized shouldComponentUpdate
implementation, but it relies on the user memoizing the children
array:
shouldComponentUpdate(nextProps: Props, nextState: State) {
return (
this.props.children !== nextProps.children ||
!fastRGLPropsEqual(this.props, nextProps, isEqual) ||
!isEqual(this.state.activeDrag, nextState.activeDrag)
);
}
If you memoize your children, you can take advantage of this, and reap faster rerenders. For example:
function MyGrid(props) {
const children = React.useMemo(() => {
return new Array(props.count).fill(undefined).map((val, idx) => {
return <div key={idx} data-grid={{ x: idx, y: 1, w: 1, h: 1 }} />;
});
}, [props.count]);
return <ReactGridLayout cols={12}>{children}</ReactGridLayout>;
}
Because the children
prop doesn't change between rerenders, updates to <MyGrid>
won't result in new renders, improving performance.
React Hooks Performance
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.
To avoid this you should wrap your WidthProvider in a useMemo:
const ResponsiveReactGridLayout = useMemo(() => WidthProvider(Responsive), []);
Custom Child Components and Draggable Handles
If you use React Components as grid children, they need to do a few things:
- Forward refs to an underlying DOM node, and
- Forward
style
,className
, onMouseDown
, onMouseUp
and onTouchEnd
to that same DOM node.
For example:
const CustomGridItemComponent = React.forwardRef(({style, className, onMouseDown, onMouseUp, onTouchEnd, children, ...props}, ref) => {
return (
<div style={{ /* styles */, ...style}} className={className} ref={ref} onMouseDown={onMouseDown} onMouseUp={onMouseUp} onTouchEnd={onTouchEnd}>
{/* Some other content */}
{children} {/* Make sure to include children to add resizable handle */}
</div>
);
}
The same is true of custom elements as draggable handles using the draggableHandle
prop. This is so that
the underlying react-draggable
library can get a reference to the DOM node underneath, manipulate
positioning via style
, and set classes.
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.
TODO List