The dashboard layout library that can show you, not tell you
Grafloria Dashboards is an MIT JavaScript dashboard layout library: draggable, resizable widgets on a grid or a splitter layout, with undo, nesting and persistence built in, for Angular, React, Vue or plain JavaScript.
moveTo, undo, addWidget, setLayout, setDragHandle, focusWidget.Reduced-motion users get the board at rest.Two layouts, one document
The grid is the gridstack model: spans, rows, push-down re-packing. The splitter is the way BI designers align widgets: one fills, the next halves, dividers are percentages. Switch live; the same JSON holds either one.
Fit never scrolls
Under sizing: 'fit' the container height is a capacity. Rows squeeze so everything stays on one screen, and an add that would need one row too many is refused where you can see it. Nothing is ever painted past the board's edge.
Drag by caption, by grip, or anywhere
One option, three answers. true makes the caption strip the only handle, so a table body scrolls and a legend clicks. A painted grip appears on the selected widget only, left, centre or right, inside the header or as a tab above the card.
Containers, as one field
A widget carrying widgets is a section with its own grid, locked in the board. Drag a KPI out of the band and the board adopts it live; drag it back and the section takes it. One undo restores the whole crossing.
Bring your charts
The kit never picks a charting library. renderWidget hands you the widget and a raw element; anything you mount there is laid out, dragged, undone and saved like the built-ins. The board on the right has one hand-drawn kind next to the six that ship.
const spec = dashboard({ widgets: [ …, { id: 'burn', kind: 'spark', span: 6, rows: 2, title: 'Burn rate', data: { values: [3, 5, 4, 8, 7, 9] } } ], renderWidget: (w, el) => { if (w.kind === 'spark') el.innerHTML = mySparkline(w.data); // yours else defaultWidgetRenderer(w, el); // the six built-ins }, });
Native in your framework
The switches are props, so a change is one call on the live board, never a remount.
<grafloria-dashboard [views]="views" [options]="{ columns: 12 }"
[layout]="layout()" [sizing]="sizing()" [static]="viewer()"
(ready)="handle = $event" style="display:block; height: 70vh" />
// a signal change is one handle call
layout = signal<'grid' | 'split'>('grid');
<GrafloriaDashboard views={views} options={{ columns: 12 }}
layout={layout} sizing={sizing} static={viewer}
onReady={setHandle} />
// setLayout('split') is one effect, never a remount
handle?.setDragHandle({ grip: true, placement: 'outside' });
<GrafloriaDashboard :views="views" :options="{ columns: 12 }"
:layout="layout" :sizing="sizing" :static="viewer"
@ready="handle = $event" />
// refs are watched; each change is one handle call
watch(grip, (v) => handle.value?.setDragHandle(v));
Angular dashboards · React dashboards · Vue dashboards · the framework tabs on the demo are the real apps.
Where it stops
This is a layout and editing library, not a BI suite: no data sources, no query builder, no scheduled reports. Where a BI designer's widget alignment comes up, it is the splitter surface the split layout reproduces, nothing more. The honest boundaries with the grid libraries are on the GridStack and react-grid-layout pages, both columns true.
Questions people ask
Is Grafloria Dashboards a gridstack alternative?
Yes for the layout: draggable, resizable widgets on a responsive grid with push-down re-packing. It adds what a layout engine leaves to you: undo per gesture, nested containers, a splitter layout, persistence of the whole board and built-in widget renderers. The honest comparison is on the GridStack alternative page, both columns true.
Does fit mode scroll?
No. Under sizing 'fit' the container height is a capacity: rows squeeze so everything stays on one screen, an add that would need one row too many is refused visibly, and nothing is ever painted past the board's edge. Sizing 'grow' keeps row heights and lets the board extend; the wheel scrolls it.
Can a widget be dragged only by a handle?
Yes. dragHandle: true makes the caption strip the only handle; a selector names your own element; { grip: true, position, placement } paints a grip on the selected widget, left, centre or right, inside the header or as a tab above the card. All of it switches live.
Does it work without React?
Yes. The kit is a plain JavaScript function, dashboard(), rendered into any element. Native components exist for Angular, React and Vue, and a web component for everything else.
Can a dashboard contain a diagram?
Yes. Dashboards and diagrams share one engine and one document format, so a diagram is a widget like any other and the same undo stack covers both.
Is any of it paid?
No. Every feature is MIT licensed; there is no pro tier.
Grafloria is MIT — every feature, no commercial tier. GridStack, react-grid-layout and DevExpress are trademarks of their respective owners; comparative references only, no affiliation implied.