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