Building a Draggable Dashboard with React Using react-grid-layout Library

Building a Draggable Dashboard with React Using react-grid-layout Library

In modern web development, creating interactive and responsive dashboards is a common requirement. React, being a popular JavaScript library for building user interfaces, offers a plethora of tools and libraries to accomplish such tasks efficiently. One such library is react-grid-layout, which provides a flexible grid layout system for React applications. In this blog post, we'll explore how to create a draggable dashboard with customizable components using react-grid-layout.

Introduction to react-grid-layout

react-grid-layout is a powerful React library that enables developers to create draggable, resizable grid layouts. It allows you to arrange UI components within a grid-based layout, providing users with the ability to customize the layout according to their preferences. With features like responsive layouts, dynamic resizing, and drag-and-drop functionality, it's an excellent choice for building interactive dashboards.

Project Overview

For this project, we'll be creating a dashboard with various components such as object alerts, driver alerts, object health, and more. Users can rearrange these components within the dashboard by dragging and dropping them, providing a personalized dashboard experience. Additionally, we'll integrate features like local storage to persist layout changes across sessions, making the dashboard more user-friendly.

Setting Up the Project

To get started, make sure you have Node.js and npm installed on your system. Create a new React project using create-react-app or any other preferred method. Once your project is set up, install the required dependencies:

npm install react-grid-layout react-resizable @mui/material @mui/icons-material

Import the required hooks and dependencies

import React, { useEffect, useState, useCallback } from "react";
import { Responsive, WidthProvider } from "react-grid-layout";
import { Box, IconButton, Paper, Typography } from "@mui/material";
import { FilterAltOutlined, SettingsOutlined } from "@mui/icons-material";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";

const ResponsiveGridLayout = WidthProvider(Responsive);

Starting the component

const App = () => {
  const [value, setValue] = useState(true);
  const initialLayout = JSON.parse(localStorage.getItem("layout")) || {
    lg: [
      { i: "a", x: 0, y: 0, w: 4, h: 1 },
      { i: "b", x: 4, y: 0, w: 2, h: 0.5 },
      { i: "c", x: 8, y: 0, w: 4, h: 0.5 },
      { i: "d", x: 0, y: 1, w: 2, h: 0.5 },
      { i: "e", x: 4, y: 1, w: 4, h: 1 },
      { i: "f", x: 8, y: 1, w: 4, h: 1 },
      { i: "g", x: 0, y: 2, w: 2, h: 0.5 },
      { i: "h", x: 2, y: 2, w: 4, h: 0.5 },
      { i: "i", x: 6, y: 2, w: 2, h: 0.5 },
      { i: "j", x: 0, y: 3, w: 6, h: 1 },
    ],
  };
  console.log("rendering");
  const [layout, setLayout] = useState(initialLayout);

  useEffect(() => {
    // Save layout to local storage whenever it changes
    localStorage.setItem("layout", JSON.stringify(layout));
  }, [layout]);

  // Function to handle layout change
  // Function to handle layout change
  const onLayoutChange = useCallback((newLayout) => {
    localStorage.setItem("layout", JSON.stringify({ lg: newLayout }));
    // setLayout({ lg: newLayout });
  }, []);

Here we have declared the state to store the initial layout values ,initially the components will be spread out in the below dimensions ,as of now i have 9 components and there are 9 initial dimensions

    lg: [
      { i: "a", x: 0, y: 0, w: 4, h: 1 },
      { i: "b", x: 4, y: 0, w: 2, h: 0.5 },
      { i: "c", x: 8, y: 0, w: 4, h: 0.5 },
      { i: "d", x: 0, y: 1, w: 2, h: 0.5 },
      { i: "e", x: 4, y: 1, w: 4, h: 1 },
      { i: "f", x: 8, y: 1, w: 4, h: 1 },
      { i: "g", x: 0, y: 2, w: 2, h: 0.5 },
      { i: "h", x: 2, y: 2, w: 4, h: 0.5 },
      { i: "i", x: 6, y: 2, w: 2, h: 0.5 },
      { i: "j", x: 0, y: 3, w: 6, h: 1 },
    ],

Whenever layout changes ,the changed layout is stored in local storage

 useEffect(() => {
    // Save layout to local storage whenever it changes
    localStorage.setItem("layout", JSON.stringify(layout));
  }, [layout]);

function to handle layout change


  // Function to handle layout change
  const onLayoutChange = useCallback((newLayout) => {
    localStorage.setItem("layout", JSON.stringify({ lg: newLayout }));
    // setLayout({ lg: newLayout });
  }, []);

So that was the functionality part,below is the jsx part

 <Box
      sx={{
        width: "98%",
        marginLeft: "1.1rem",
        marginTop: "0.8rem",
        height: "98.2vh",
        overflowY: "auto",
      }}
      component={Paper}
    >
      {/* <Navbar /> */}
      <Box
        sx={{
          width: "98.1%",
          backgroundColor: "#b5592a",
          display: "flex",
          justifyContent: "space-between",
          padding: "1rem",
          alignItems: "center",
          top: 0,
          position: "sticky",
          zIndex: "100",
        }}
      >
        <Typography
          sx={{ color: "white", fontSize: "1.4rem", fontWeight: "500" }}
        >
          Dashboard
        </Typography>
        <Box>
          <IconButton>
            <FilterAltOutlinedIcon
              sx={{ color: "white", height: "2rem", width: "2.5rem" }}
            />
          </IconButton>
          <IconButton>
            <SettingsOutlinedIcon
              sx={{ color: "white", height: "2rem", width: "2.5rem" }}
            />{" "}
          </IconButton>
        </Box>
      </Box>
      <ResponsiveGridLayout
        className="layout"
        layouts={layout}
        breakpoints={{ lg: 1200 }}
        cols={{ lg: 12 }}
        rowHeight={410}
        width={1200}
        isResizable={false}
        onLayoutChange={onLayoutChange}
      >
        <Box key="a" component={Paper}>
          {/* <DemoComponent color={"yellow"} /> */}
          <ObjectAlerts />
        </Box>
        <Box key="b" component={Paper} style={{ backgroundColor: " #7dd87d" }}>
          {/* <DemoComponent color={"green"} /> */}
          <Address />
        </Box>
        <Box key="c" component={Paper}>
          {/* <DemoComponent color={"red"} /> */}
          <Fleet />
        </Box>
        <Box key="d" component={Paper} style={{ backgroundColor: "#E6E6FA" }}>
          {/* <DemoComponent color={"blue"} /> */}
          <Fence />
        </Box>
        <Box
          key="e"
          component={Paper}
          //  style={{ backgroundColor: "violet" }}
        >
          {/* <DemoComponent color={"violet"} /> */}
          <DriverAlerts />
        </Box>
        <Box
          key="f"
          component={Paper}
          // style={{ backgroundColor: "lemonchiffon" }}
        >
          {/* <DemoComponent color={"lemonchiffon"} /> */}
          <ObjectHealth />
        </Box>
        <Box key="g" component={Paper} style={{ backgroundColor: "#aef4a4" }}>
          {/* <DemoComponent color={"blue"} /> */}
          <Zone />
        </Box>
        <Box
          key="h"
          component={Paper}
          // style={{ backgroundColor: "green" }}
        >
          {/* <DemoComponent color={"green"} /> */}
          <ObjectMode />
        </Box>
        <Box key="i" component={Paper} style={{ backgroundColor: "#fff" }}>
          {/* <DemoComponent color={"red"} /> */}
          <Average />
        </Box>
      </ResponsiveGridLayout>
    </Box>

here i have a kind of navbar and below that i am rendering the draggable dashboard components

Refer to the link for code

https://github.com/nikhilgithub911/drag_n_drop