DEV Community

Cover image for You should really use react-grid-layout, it's awesome !
Joffrey Mischler
Joffrey Mischler

Posted on

You should really use react-grid-layout, it's awesome !

If you do any self-hosting, home automation, or IoT projects, you know all about the MQTT protocol. It’s ultra-lightweight and robust. But on the UI side, you often end up either scripting your own custom interfaces or building dashboards by hand from scratch.

That’s exactly why I developed mqtt-dashboard, a self-hostable dashboard featuring a Go backend and a React frontend. The goal? To easily create custom panels, subscribe to MQTT topics, and display that data cleanly.

But very quickly, I hit a classic UX problem: how do I let the user organize their panels however they want? I wanted them to be able to drag, drop, and resize every single widget on the fly—true drag-and-drop—without breaking the layout or making the codebase a total nightmare to manage.

Enter React-Grid-Layout.


The Problem with Static Dashboards

At first, you think, "Easy, I'll just set up a CSS grid or use Flexbox, add some 'Move Up / Move Down' buttons, and call it a day." But in the real world, that user experience is awful. A real dashboard needs to feel alive. You want your CPU monitoring panel big and bold in the top left, and your light switch tucked away in a small corner.

Trying to build that yourself in React (handling mouse events, recalculating X/Y coordinates in pixels, managing object collisions so panels don't overlap...) is a one-way ticket to wasting three weeks of dev time just to end up with pure spaghetti code.

Why React-Grid-Layout?

React-Grid-Layout (RGL) is a grid layout library built specifically for React. It handles responsive design, drag-and-drop, and resizing natively out of the box.

What I love about this tool is how it abstracts away all the math. Instead of dealing with pixels, you think in grid coordinates (columns and rows).

Here’s a look at the core structure I used in mqtt-dashboard to display the widgets:

import RGL, { WidthProvider } from "react-grid-layout";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";

const ResponsiveReactGridLayout = WidthProvider(RGL);

function Dashboard({ widgets, onLayoutChange }) {
  // The layout defines the position and size of each panel
  const layout = widgets.map(widget => ({
    i: widget.id,      // Unique widget ID
    x: widget.x,       // X position on the grid
    y: widget.y,       // Y position on the grid
    w: widget.w,       // Width (number of columns)
    h: widget.h,       // Height (number of rows)
    minW: 2,           // Safety check so widgets don't shrink into unreadability
    minH: 2
  }));

  return (
    <ResponsiveReactGridLayout // 12-column Callback Dragging Height Standard a around className="layout" cols="{12}" draggableHandle=".widget-drag-handle" header in is layout move of onLayoutChange="{onLayoutChange}" pixels restricted row rowHeight="{50}" single split the things to when>
      {widgets.map(widget => (
        <div key={widget.id} className="widget-container">
          <div className="widget-drag-handle" aria-label="Drag handle for widget">:: Grab me</div>
          <MQTTWidgetContent topic="{widget.topic}" type="{widget.type}"/>
        </div>
      ))}
    </ResponsiveReactGridLayout>
  );
}
Enter fullscreen mode Exit fullscreen mode

Pitfalls to Avoid (The Reality Check)

On paper, you import the package and bam, it works. But as usual, once you move into production, reality kicks in. If you're planning to use it, keep these things in mind:

1. The React key Trap

It’s written in bold in the documentation, but we all fall for it the first time: the key of the child component must perfectly match the i property in your layout object. If you use a randomly generated string or a different identifier, React-Grid-Layout loses track, the drag mechanics go crazy, and your panels start randomly teleporting across the screen.

2. Saving State (Without Spamming Your Backend)

The component provides an onLayoutChange callback. This fires every single time a panel moves even one grid unit. If you send a fetch or an HTTP request to your API for every millisecond of dragging, your backend is going to absolutely choke.

  • The Fix: Update your local React state immediately to keep the UI smooth, but use a debounce function before sending the final layout payload to your Go server.

3. Fighting with Widget Content

If your panel contains interactive elements like charts (Chart.js or Recharts) or text that users need to highlight, the global drag-and-drop layer can block interactions inside the widget.

  • The Fix: Make good use of the draggableHandle option. This restricts the dragging capability to a specific area (like a small title bar at the top of the panel), leaving the rest of the widget free for clicking buttons or scrolling.

Verdict

If you need to build a dashboard, a monitoring SaaS, or any tool where the user needs full control over their workspace, React-Grid-Layout saves you an insane amount of time. It runs smoothly, feels incredibly responsive, and when paired with a lightweight backend, gives you a genuinely dynamic app.

If you want to see exactly how I wired this up with state management, the full code is open source here: jmischler72/mqtt-dashboard. Drop by the repo, check out the code, fork it, or hit me up in the comments if you have any questions!

What are you guys using to handle dynamic grid layouts in React?

Take care, Ciao!

Top comments (0)