This page looks best with JavaScript enabled

How to Use the React-Grid-Layout to Create a Responsive, Draggable Grid

 ·  ☕ 9 min read  ·  ✍️ Iskander Samatov

react-grid-layout tutorial


Recently I needed to build a robust, fully-responsive grid that supports dragging for the project I’m working on - YourTrail . After quite a bit of research, I picked React-Grid-Layout for various reasons.

This post is a deep dive into the library with a step-by-step tutorial on setting up a responsive grid.

react-grid-layout final result

Why use React-Grid-Layout

React-Grid-Layout is a powerful React library that allows you to create grids with absolute control over the layout. You can customize everything from the column and row widths, to how cells are positioned within the grid.

With that said, Reac-Grid-Layout is not the only option out there. You can use other grid libraries, like gridstack or gridster . So let’s cover why React-Grid-Layout might be a better option for your project.

Written specifically for React. React-Grid-Layout is a library written specifically for React that provides a familiar component-based API. It also doesn’t require any additional dependencies, like jQuery.

Supports responsive UI and breakpoints. React-Grid-Layout makes it easy to add support for mobile. You can specify different breakpoints and configure how your grid behaves at each one. This makes it easy to create layouts that look good on all devices.

TypeScript support. React-Grid-Layout provides TypeScript support with DefinitelyTyped , which helps write more robust code.

Setting up the basic dashboard

Now let’s get started with the tutorial. We’ll use styled-components to style our grid, but the styling will be minimal.

First, install the package:
npm install react-grid-layout

Then import the GridLayout component:
import GridLayout from "react-grid-layout";

The component accepts any DOM node as children, so we don’t need to use any specific grid item components. Here’s the overview of the props this grid needs to work:

  • layout - an array of objects that specify the location of items on the grid. This prop will dictate how the items on your grid are positioned.
  • cols - the number of columns the grid will have
  • width - the width of the grid in pixels. We’ll hardcode this value for now, but in the next section, we’ll take a look at the responsive solution.
  • rowHeight - the height of each row. This number must be static for the grid to function correctly. However, you can still have variable-height items by specifying the different number of rows each item takes.

Let’s set up a basic grid with some dummy data and see what we have:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import GridLayout from "react-grid-layout";
import styled from "styled-components";

const layout = [
  { i: "blue-eyes-dragon", x: 0, y: 0, w: 1, h: 1 },
  { i: "dark-magician", x: 1, y: 0, w: 1, h: 1 },
  { i: "kuriboh", x: 2, y: 0, w: 1, h: 1 },
  { i: "spell-caster", x: 3, y: 0, w: 1, h: 1 },
  { i: "summoned-skull", x: 4, y: 0, w: 1, h: 1 }
];

const GridItemWrapper = styled.div`
  background: #f5f5f5;
`;

const GridItemContent = styled.div`
  padding: 8px;
`;

const Root = styled.div`
  padding: 16px;
`;

export const Grid = () => {
  return (
    <Root>
      <GridLayout layout={layout} cols={5} rowHeight={300} width={1000}>
        <GridItemWrapper key="blue-eyes-dragon">
          <GridItemContent>Blue Eyes Dragon</GridItemContent>
        </GridItemWrapper>
        <GridItemWrapper key="dark-magician">
          <GridItemContent>Dark Magician</GridItemContent>
        </GridItemWrapper>
        <GridItemWrapper key="kuriboh">
          <GridItemContent>Kuriboh</GridItemContent>
        </GridItemWrapper>
        <GridItemWrapper key="spell-caster">
          <GridItemContent>Spell Caster</GridItemContent>
        </GridItemWrapper>
        <GridItemWrapper key="summoned-skull">
          <GridItemContent>Summoned Skull</GridItemContent>
        </GridItemWrapper>
      </GridLayout>
    </Root>
  );
};

Our grid has five columns and a rowHeight value of 300 pixels. Notice the format of the items in the layout: each item has a unique id i, width, height, and x and y position.

GridLayout matches items with their layout information using the key property. So make sure the key matches i of the corresponding layout item.

Here’s what the initial grid would look like:

grid layout first iteration

Dragging

Luckily, dragging comes out of the box in react-grid-layout and is turned on by default. In order to disable it, you need to set the draggable={false} for both GridLayout and its children items.

Another thing that comes by default is compacting a messy layout. It helps when you have items stacked upon each other in an unorganized way. The compacting is triggered when the layout changes due to adding/removing grid items or resizing the browser window.

You can configure the compacting logic using the compactType prop. The possible values are horizontal | vertical. It’s set to vertical by default.

Here’s what compacting looks like in action:

react-grid-layout compacting in action

Notice how the item on the second row shifts to the top as soon as we moved the item that was before it.

Making it responsive

Now that we have our basic grid working, it’s time to add responsiveness to make it reduce or increase the number of columns based on the width of the screen. For that to happen, we need to listen to changes in screen size and provide a different layout object based on the current width of the screen. Luckily, React-Grid-Layout provides a solution for this - the WidthProvider HOC .

Here’s how to use it:

1
2
3
import { Responsive, WidthProvider } from "react-grid-layout";

const ResponsiveGridLayout = WidthProvider(Responsive);

We created a ResponsiveGridLayout grid by wrapping the Responsive grid component with the WidthProvider HOC.

The difference between the Responsive component and GridLayout we used earlier is that the Responsive layout uses breakpoints and multiple different layout objects to configure the grid based on the screen width.

WidthProvider automatically listens to the screen width changes and notifies Responsive when it needs to adjust the layout.

Now let’s use our ResponsiveGridLayout to setup our responsive grid with breakpoints:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import { Responsive, WidthProvider } from "react-grid-layout";
const ResponsiveGridLayout = WidthProvider(Responsive);

const layout = [
  { i: "blue-eyes-dragon", x: 0, y: 0, w: 1, h: 1 },
  { i: "dark-magician", x: 1, y: 0, w: 1, h: 1 },
  { i: "kuriboh", x: 2, y: 0, w: 1, h: 1 },
  { i: "spell-caster", x: 3, y: 0, w: 1, h: 1 },
  { i: "summoned-skull", x: 4, y: 0, w: 1, h: 1 }
];

// styled-components definition removed for brevity...
export const Grid = () => {
  return (
    <Root>
      <ResponsiveGridLayout
        layouts={{ lg: layout }}
        breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
        cols={{ lg: 5, md: 4, sm: 3, xs: 2, xxs: 1 }}
        rowHeight={300}
        width={1000}
      >
        <GridItemWrapper key="blue-eyes-dragon">
          <GridItemContent>Blue Eyes Dragon</GridItemContent>
        </GridItemWrapper>
        <GridItemWrapper key="dark-magician">
          <GridItemContent>Dark Magician</GridItemContent>
        </GridItemWrapper>
        <GridItemWrapper key="kuriboh">
          <GridItemContent>Kuriboh</GridItemContent>
        </GridItemWrapper>
        <GridItemWrapper key="spell-caster">
          <GridItemContent>Spell Caster</GridItemContent>
        </GridItemWrapper>
        <GridItemWrapper key="summoned-skull">
          <GridItemContent>Summoned Skull</GridItemContent>
        </GridItemWrapper>
      </ResponsiveGridLayout>
    </Root>
  );
};

We added a new prop called breakPoints that defines our custom breakpoints. Note that these breakpoints are for the width of the grid component, not the whole screen.

cols prop provides the number of columns each breakpoint needs to have. Our maximum is five columns, and the minimum is one. This ensures that our grid looks good on tablets and mobile devices.

Instead of using the layout prop, we’re using layouts to provide a separate layout for each breakpoint. Also, notice how we’ve only specified the layout for the lg breakpoint. You don’t have to provide layouts for all breakpoints, just lg. If you omit the other ones, react-grid-layout will calculate them based on the compacting rules.

Here’s what our new responsive grid looks like when you resize it:

react-grid-layout resizing in action

Persisting data

So far, so good. We have a responsive grid that we can drag and resize. But what if we want to save the layout so that when the user comes back, the items are in the same position?

This is where data persistence comes into play. Where exactly you keep the data depends on your application requirements. But generally speaking, we want to save the layout object for later use. In this tutorial, we’ll save our layout in local storage.

With react-grid-layout persisting data is as easy as adding the onLayoutChange listener:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import { Responsive, WidthProvider } from "react-grid-layout";
import styled from "styled-components";

const ResponsiveGridLayout = WidthProvider(Responsive);

const layout = [
  { i: "blue-eyes-dragon", x: 0, y: 0, w: 1, h: 1 },
  { i: "dark-magician", x: 1, y: 0, w: 1, h: 1 },
  { i: "kuriboh", x: 2, y: 0, w: 1, h: 1 },
  { i: "spell-caster", x: 3, y: 0, w: 1, h: 1 },
  { i: "summoned-skull", x: 4, y: 0, w: 1, h: 1 }
];

const getLayouts = () => {
  const savedLayouts = localStorage.getItem("grid-layout");

  return savedLayouts ? JSON.parse(savedLayouts) : { lg: layout };
};
// styled-components definition removed for brevity...
export const Grid = () => {
  const handleLayoutChange = (layout, layouts) => {
  localStorage.setItem("grid-layout", JSON.stringify(layouts));
};

return (
  <Root>
    <ResponsiveGridLayout
      layouts={getLayouts()}
      breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
      cols={{ lg: 5, md: 4, sm: 3, xs: 2, xxs: 1 }}
      rowHeight={300}
      width={1000}
      onLayoutChange={handleLayoutChange}
    >
      <GridItemWrapper key="blue-eyes-dragon">
        <GridItemContent>Blue Eyes Dragon</GridItemContent>
      </GridItemWrapper>
      <GridItemWrapper key="dark-magician">
        <GridItemContent>Dark Magician</GridItemContent>
      </GridItemWrapper>
      <GridItemWrapper key="kuriboh">
        <GridItemContent>Kuriboh</GridItemContent>
      </GridItemWrapper>
      <GridItemWrapper key="spell-caster">
        <GridItemContent>Spell Caster</GridItemContent>
      </GridItemWrapper>
      <GridItemWrapper key="summoned-skull">
        <GridItemContent>Summoned Skull</GridItemContent>
      </GridItemWrapper>
    </ResponsiveGridLayout>
  </Root>
);
};

Our handleLayoutChange handler is triggered each time the layout of the grid changes. The handler calls localStorage.setItem to store the new layouts object.

The next time we render our grid, we can use this data to populate the initial state of our grid items. And that’s precisely what getLayouts does. It returns the saved layouts object used to populate our grid with the correct data.

Now, if you reload the page and resize the browser window, you’ll see that the items in the grid stay in their respective positions:

react-grid-layout persisting layout

Conclusion

In this post, we’ve seen how easy it is to use react-grid-layout to create a responsive grid. We’ve also learned how to persist the layout so that the grid items stay in their respective positions when the user reloads the page.

If you’d like to get more web development, React and TypeScript tips consider following me on Twitter where I share things as I learn them.
Happy coding!

Share on

Software Development Tutorials
WRITTEN BY
Iskander Samatov
The best up-to-date tutorials on React, JavaScript and web development.