A react-grid-layout alternative for dashboards that outgrow the grid — and React

Honest framing first: react-grid-layout is the standard React dashboard grid — draggable, resizable, responsive breakpoints, MIT, millions of weekly installs — and if you are all-React and a layout engine is all you need, use it. Grafloria Dashboards is for the moment the grid becomes an application: undo, nesting, persistence of the whole board, a splitter layout, and the same board in Angular and Vue.

The comparison here is with React grid layout engines. Where a BI product such as DevExpress Dashboard comes up below, it is only for how its designer aligns widgets — a splitter surface that is always covered — which the kit's split layout reproduces. Data sources, query builders and the rest of a BI suite are out of scope on both sides of this table.

When to stay with react-grid-layout

You are all-React and need exactly a grid: place, drag, resize, compact, responsive breakpoints with a layout per breakpoint. react-grid-layout does all of it, small and focused, and its item array is plain state you already own.

Feature by feature

✓ built in · DIY possible, but yours to build · — out of scope. Both columns are meant to be true, including the rows we lose.

react-grid-layoutGrafloria Dashboards
Grid & layout
Drag / resize / re-pack✓ — compactType vertical / horizontal / none✓ push-down with skip, swap shapes, truthful placeholder
Nested grids✓ containers — cross-boundary drag with adoption, one undo step
Responsive columnsResponsiveGridLayout, a layout per breakpoint✓ cells re-flow, not just clamp
Locked tilesstatic, isDraggable/isResizable per itempinned, movable, resizable — pinned survives every reflow
Fluid width — 100% of the containerWidthProvider✓ the default; mode: 'fixed' keeps an authored world for a board inside a diagram
Per-widget min / max sizeminW … maxHlimits: { minSpan, maxSpan, minRows, maxRows }
Resize from any edgeresizeHandles✓ every edge and corner, the cursor says which
Static (read-only) gridisDraggable={false} + isResizable={false}static: true, switched live with setStatic()
Drag handledraggableHandle / draggableCanceldragHandle — the caption strip, your own selector, or a painted grip (left / center / right, in the header or a tab above the card) shown on the selected widget only; switched live
Sizing modesrowHeight, maxRows, autoSizesizing: 'grow' — rows keep their height, the board extends; 'fit' — rows squeeze, the board never scrolls; switched live
Splitter layout — the surface is always coveredlayout: 'split' — the way a BI dashboard designer (DevExpress's, for one) aligns widgets: one fills, the next halves, dividers are percentages, a drop lands on an edge; switched live, same document
Bounded heightmaxRows✓ fit mode is a capacity — a board never paints past its own edge
Keyboard operationnot documented✓ arrows move, Shift+arrows resize, every outcome announced — axe-core in CI
Touch✓ drag and resize, 24-px handles visible without hover
Widgets & content
Content modelchildren keyed by i — rendering is yourskind + data — widgets are data
Built-in renderers (KPI, line, donut…)✓ six, themed
Your own chartsDIY as childrenrenderWidget seam — mount anything
Editing
Undo / redo✓ every gesture one command, ⌘Z included
Cross-container gestures✓ adoption + escalation, one undo
Persistence & sharing
Save / loadonLayoutChange gives you the layout array✓ the whole board — widgets, nesting, data
Export PNG / SVG / PDF✓ client and server-side
Diagrams in the same document✓ one engine, one format, one undo stack
Real-time collaboration✓ per-property CRDT, transport-agnostic
Project
LicenseMITMIT — no pro tier
Dependenciesreact-draggable, react-resizablea handful; ELK layout is a lazy chunk (sizes)
Framework wrappersReact only✓ native components, same three + web component
Maturity✓ a decade in production, one of the most installed React librariesnewer — every claim above is a CI-tested demo you can open

The same dashboard, both ways

react-grid-layout lays out children you render; the layout array is your state:

import GridLayout from 'react-grid-layout';

const layout = [
  { i: 'rev',   x: 0, y: 0, w: 3, h: 1 },
  { i: 'trend', x: 3, y: 0, w: 8, h: 2 },
];
<GridLayout layout={layout} cols={12} rowHeight={30} width={1200} onLayoutChange={save}>
  <div key="rev" className="kpi">Total revenue</div>
  <div key="trend"><canvas id="trend" /></div>   {/* chart wiring is yours */}
</GridLayout>

Grafloria declares the widgets too — the chart is the declaration — and in React it is one component:

import { GrafloriaDashboard } from '@grafloria/react';
import { dashboard } from '@grafloria/dashboard';

// <GrafloriaDashboard views={views} layout="grid" sizing="fit" onReady={h => (handle = h)} />
// …or without React, the same board:
const SPEC = dashboard({
  columns: 12,
  widgets: [
    { id: 'rev',   kind: 'kpi',  span: 3, rows: 1,
      data: { label: 'Total revenue', value: '$6.81M', delta: 12.4 } },
    { id: 'trend', kind: 'line', span: 8, rows: 2, title: 'Revenue vs target',
      data: { series: [{ name: 'Revenue', values: [420,455,512,588,648] }],
              labels: ['Jan','Feb','Mar','Apr','May'] } },
  ],
});
render(SPEC, document.getElementById('canvas'));
// SPEC.handle.toJSON() round-trips the WHOLE board — widgets, nesting, data

How the concepts map

react-grid-layoutGrafloria Dashboards
x, y, w, h on a layout itemx, y, span, rows — or omit them and widgets flow in declaration order
children keyed by ikind + data — six built-in renderers (KPI, line, donut…), or your own via renderWidget
onLayoutChange(layout) / the layout proptoJSON()dashboard() — one document format shared with diagrams
no equivalenta widget carrying widgets — cross-boundary drag with adoption, one undo step
static: true on an itempinned: true — never pushed, survives every reflow
cols per breakpoint, layouts per breakpointcolumns + responsive re-flow of the cells themselves
isDraggable={false} isResizable={false}static: true — the viewer's mode; the API and undo keep working
draggableHandle selectordragHandle: true | '.sel' | { grip: true, position, placement } — caption strip, your element, or a grip on the selected widget
no equivalentlayout: 'split' — a splitter tree beside the grid, setLayout() keeps the picture either way
minW / maxW / minH / maxHlimits: { minSpan, maxSpan, minRows, maxRows } on the widget
maxRowssizing: 'fit' — the board's height is the capacity; overflow: 'scroll' opts out

See the difference in one demo

The clearest way to judge the boundary: react-grid-layout gives you the grid geometry; this demo is a dashboard whose sections are containers — drag a tile across a section boundary and the other grid adopts it live, one undo restores the whole gesture, and toJSON() serializes the nesting. The grid options demo shows column counts, packing and responsive re-flow driven as data, and the fluid dashboard has Fit / Grow, Grid / Split and the drag-handle modes in one toolbar.

Pricing and feature cells reflect vendor pages and shipped type definitions as of July–September 2026 — always confirm on the vendor's site. Corrections welcome on GitHub; this page aims to stay honest, not to win.