Dashboard Layout
A grid-based dashboard engine. Drag widgets to rearrange, drag the corners to resize, hide / show widgets from a built-in panel, save per-breakpoint layouts to localStorage, define named presets, expose per-widget refresh and settings — all themed and dark-mode ready.
Basic Usage
Provide widgets + per-breakpoint defaultLayouts. Toggle Edit layout in the toolbar to enable drag and resize.
Basic Dashboard
Click 'Edit layout' to drag widget headers, resize from the corners, and toggle visibility. Use the maximize / collapse buttons on each widget for focused views.
import { DashboardLayout } from 'fluxo-ui';
import type { DashboardWidget, DashboardLayouts } from 'fluxo-ui';
const widgets: DashboardWidget[] = [
{ id: 'sales', title: 'Sales', icon: LineChartUpIcon, children: <Sales /> },
{ id: 'visitors', title: 'Visitors', icon: UsersIcon, children: <Visitors /> },
{ id: 'channels', title: 'Channels', icon: PieChartIcon, children: <Channels /> },
// ...
];
const defaultLayouts: DashboardLayouts = {
lg: [
{ id: 'sales', x: 0, y: 0, w: 4, h: 4 },
{ id: 'visitors', x: 4, y: 0, w: 4, h: 4 },
{ id: 'channels', x: 8, y: 0, w: 4, h: 4 },
// ...
],
};
<DashboardLayout
widgets={widgets}
defaultLayouts={defaultLayouts}
toolbarTitle="Overview"
/>LocalStorage Persistence
Pass a persistKey and the full state — layouts, hidden ids, collapsed ids, maximized id, active preset — is saved automatically.
LocalStorage Persistence
Pass a persistKey and the full DashboardLayoutState (layouts, hidden, collapsed, maximized, preset) is saved automatically. Drag a widget, refresh the page, and watch it come back.
localStorage["fluxo-dashboard-demo"]. Use the Reset button in the toolbar to restore defaults.<DashboardLayout
widgets={widgets}
defaultLayouts={defaultLayouts}
persistKey="my-app-dashboard"
toolbarTitle="My Dashboard"
/>Layout Presets
Define named layout/visibility presets and the toolbar exposes a dropdown for switching between them.
Layout Presets
Define named layouts (e.g. 'Sales focus', 'Operations'). Users pick from the Presets menu. Each preset can override visibility AND positions.
const presets: DashboardLayoutPreset[] = [
{
id: 'default',
name: 'Default',
description: 'Balanced overview',
layouts: defaultLayouts,
},
{
id: 'sales-focus',
name: 'Sales focus',
description: 'Pin sales + orders, hide others',
hiddenIds: ['channels', 'team', 'health', 'calendar'],
layouts: {
lg: [
{ id: 'sales', x: 0, y: 0, w: 6, h: 6 },
{ id: 'orders', x: 6, y: 0, w: 6, h: 6 },
{ id: 'visitors', x: 0, y: 6, w: 12, h: 5 },
],
},
},
];
<DashboardLayout widgets={widgets} presets={presets} ... />Widget Chrome & States
Pick a visual chrome per widget; surface badges and error states declaratively.
Widget Chrome & States
Each widget can pick its visual chrome and surface badges, errors, and loading states without extra wiring.
const widgets: DashboardWidget[] = [
{ id: 'a', title: 'Card chrome', chrome: 'card', children: <Demo /> },
{ id: 'b', title: 'Borderless chrome', chrome: 'borderless', children: <Demo /> },
{ id: 'c', title: 'Sunken chrome', chrome: 'sunken', children: <Demo /> },
{ id: 'd', title: 'Plain chrome', chrome: 'plain', children: <Demo /> },
{ id: 'e', title: 'With badge', badge: <span>NEW</span>, children: <Demo /> },
{ id: 'f', title: 'With error', error: 'Service unavailable', children: <Demo /> },
];Custom Header Controls
Pass any React node to headerActions — filter toggles, dropdowns, export buttons, share links, anything. Pointer events inside the slot don't initiate drag. Wrap your controls in elements with class eui-dashboard-widget-action to inherit the built-in icon-button styling.
Custom Header Actions & Collapse Compaction
Pass any React node to headerActions to add filter toggles, dropdowns, export buttons, share links — anything. Collapsing a widget shrinks it to its header bar and the layout compacts vertically, so widgets below float up.
eui-dashboard-widget-action to inherit the built-in icon-button styling. Pointer events inside the header actions slot do not start a drag.<DashboardWidget
id="sales"
title="Sales"
headerActions={
<>
<button className="eui-dashboard-widget-action" aria-label="Filter">
<FilterIcon width={14} height={14} aria-hidden="true" />
</button>
<Dropdown
value={range}
options={[{ label: '7 days', value: '7d' }, { label: '30 days', value: '30d' }]}
onChange={setRange}
size="xs"
/>
<button className="eui-dashboard-widget-action" aria-label="Export">
<DownloadIcon width={14} height={14} aria-hidden="true" />
</button>
</>
}
>
...
</DashboardWidget>Refresh & Settings
Hand a widget an onRefresh Promise to enable a spinning refresh icon, and a renderSettings render-prop to expose a settings popover.
Per-widget Refresh & Settings
Hand each widget an onRefresh handler to enable the refresh icon (the icon spins while the promise resolves) and a renderSettings function to expose a settings popover.
const widgets: DashboardWidget[] = [
{
id: 'sales',
title: 'Sales',
renderSettings: ({ closeSettings }) => (
<>
<label><input type="checkbox" /> Compare to prev period</label>
<button onClick={closeSettings}>Save</button>
</>
),
onRefresh: async () => {
await fetch('/api/sales').then(/*...*/);
},
children: <Sales />,
},
];Import
import { DashboardLayout } from 'fluxo-ui';
import type {
DashboardWidget,
DashboardLayouts,
DashboardLayoutPreset,
DashboardLayoutState,
} from 'fluxo-ui';DashboardLayout Props
widgetsreqDashboardWidget[]All widgets that can appear on the dashboard. Hidden ones are listed in the Add Widget panel.
widgetsreqDashboardWidget[]All widgets that can appear on the dashboard. Hidden ones are listed in the Add Widget panel.
layoutsDashboardLayoutsControlled per-breakpoint layouts: { lg: WidgetLayout[], md: ..., ... }. Pair with onLayoutChange.
layoutsDashboardLayoutsControlled per-breakpoint layouts: { lg: WidgetLayout[], md: ..., ... }. Pair with onLayoutChange.
defaultLayoutsDashboardLayoutsInitial per-breakpoint layouts (uncontrolled). Auto-generated if omitted.
defaultLayoutsDashboardLayoutsInitial per-breakpoint layouts (uncontrolled). Auto-generated if omitted.
onLayoutChange(payload: DashboardLayoutChangePayload) => voidFires on every layout, visibility, collapse, maximize, preset or reset change.
onLayoutChange(payload: DashboardLayoutChangePayload) => voidFires on every layout, visibility, collapse, maximize, preset or reset change.
breakpointsDashboardBreakpoint[]"xl/lg/md/sm/xs"Custom breakpoint definitions: { key, minWidth, columns, label? }.
breakpointsDashboardBreakpoint[]"xl/lg/md/sm/xs"Custom breakpoint definitions: { key, minWidth, columns, label? }.
rowHeightnumber"64"Pixel height of one grid row.
rowHeightnumber"64"Pixel height of one grid row.
margin[number, number]"[12, 12]"Horizontal and vertical gap between widgets in pixels.
margin[number, number]"[12, 12]"Horizontal and vertical gap between widgets in pixels.
containerPadding[number, number]"[12, 12]"Padding inside the grid container in pixels.
containerPadding[number, number]"[12, 12]"Padding inside the grid container in pixels.
minWidgetWidthnumber"1"Minimum column span for any widget.
minWidgetWidthnumber"1"Minimum column span for any widget.
minWidgetHeightnumber"1"Minimum row span for any widget.
minWidgetHeightnumber"1"Minimum row span for any widget.
editModebooleanControlled edit mode. When true, drag/resize handles and toolbar actions are enabled.
editModebooleanControlled edit mode. When true, drag/resize handles and toolbar actions are enabled.
defaultEditModeboolean"false"Initial edit mode (uncontrolled).
defaultEditModeboolean"false"Initial edit mode (uncontrolled).
onEditModeChange(editing: boolean) => voidFires when the user toggles edit mode.
onEditModeChange(editing: boolean) => voidFires when the user toggles edit mode.
persistKeystringlocalStorage key for automatic save/restore of the full DashboardLayoutState.
persistKeystringlocalStorage key for automatic save/restore of the full DashboardLayoutState.
presetsDashboardLayoutPreset[]Named layout presets to expose in the toolbar Presets menu.
presetsDashboardLayoutPreset[]Named layout presets to expose in the toolbar Presets menu.
onPresetChange(preset: DashboardLayoutPreset | null) => voidFires when the user selects a preset.
onPresetChange(preset: DashboardLayoutPreset | null) => voidFires when the user selects a preset.
showToolbarboolean"true"Show the built-in toolbar (Edit, Add Widget, Reset, Presets).
showToolbarboolean"true"Show the built-in toolbar (Edit, Add Widget, Reset, Presets).
toolbarTitlestringTitle rendered on the left of the toolbar (also used as the grid's aria-label).
toolbarTitlestringTitle rendered on the left of the toolbar (also used as the grid's aria-label).
toolbarSlotStartReact.ReactNodeCustom content rendered after the toolbar title.
toolbarSlotStartReact.ReactNodeCustom content rendered after the toolbar title.
toolbarSlotEndReact.ReactNodeCustom content rendered before the Edit button on the right side of the toolbar.
toolbarSlotEndReact.ReactNodeCustom content rendered before the Edit button on the right side of the toolbar.
addWidgetLabelstringOverride the Add Widget button label.
addWidgetLabelstringOverride the Add Widget button label.
compactType'vertical' | 'horizontal' | null"'vertical'"Auto-compaction direction after drag/resize. null disables compaction.
compactType'vertical' | 'horizontal' | null"'vertical'"Auto-compaction direction after drag/resize. null disables compaction.
allowOverlapboolean"false"Allow widgets to overlap (skip collision push-down).
allowOverlapboolean"false"Allow widgets to overlap (skip collision push-down).
emptyStateReact.ReactNodeCustom empty state when there are no visible widgets.
emptyStateReact.ReactNodeCustom empty state when there are no visible widgets.
localePartial<DashboardLayoutLocale>Override any built-in toolbar / action label.
localePartial<DashboardLayoutLocale>Override any built-in toolbar / action label.
classNamestringClass name applied to the outer wrapper.
classNamestringClass name applied to the outer wrapper.
styleReact.CSSPropertiesInline styles for the outer wrapper.
styleReact.CSSPropertiesInline styles for the outer wrapper.
widgetClassNamestringClass name applied to every widget surface.
widgetClassNamestringClass name applied to every widget surface.
DashboardWidget Props
idreqstringUnique widget id; referenced by layouts and presets.
idreqstringUnique widget id; referenced by layouts and presets.
titlereqstringWidget title shown in the header bar and Add Widget panel.
titlereqstringWidget title shown in the header bar and Add Widget panel.
iconSVGIconOptional header icon (from fluxo-ui/icons).
iconSVGIconOptional header icon (from fluxo-ui/icons).
descriptionstringShort description shown in the Add Widget panel.
descriptionstringShort description shown in the Add Widget panel.
categorystringOptional grouping label for future categorization.
categorystringOptional grouping label for future categorization.
chrome'card' | 'plain' | 'borderless' | 'sunken'"'card'"Visual chrome around the widget.
chrome'card' | 'plain' | 'borderless' | 'sunken'"'card'"Visual chrome around the widget.
headerActionsReact.ReactNodeCustom icons / buttons / dropdowns rendered to the left of the built-in widget actions (refresh, settings, collapse, maximize, remove). Wrap them in elements with class `eui-dashboard-widget-action` to inherit the built-in button styling, or use your own controls. Pointer events inside this slot do not trigger drag.
headerActionsReact.ReactNodeCustom icons / buttons / dropdowns rendered to the left of the built-in widget actions (refresh, settings, collapse, maximize, remove). Wrap them in elements with class `eui-dashboard-widget-action` to inherit the built-in button styling, or use your own controls. Pointer events inside this slot do not trigger drag.
renderSettings(ctx: WidgetSettingsContext) => React.ReactNodeRender-prop for the per-widget settings popover. Setting this enables the gear icon.
renderSettings(ctx: WidgetSettingsContext) => React.ReactNodeRender-prop for the per-widget settings popover. Setting this enables the gear icon.
onRefresh() => void | Promise<void>Setting this enables the refresh icon. While the promise is pending the icon spins.
onRefresh() => void | Promise<void>Setting this enables the refresh icon. While the promise is pending the icon spins.
loadingbooleanForce the loading overlay independent of refresh state.
loadingbooleanForce the loading overlay independent of refresh state.
errorstring | nullDisplay an error block in place of the body content.
errorstring | nullDisplay an error block in place of the body content.
lastUpdatedDate | string | numberShown in the widget footer as 'Updated: …'.
lastUpdatedDate | string | numberShown in the widget footer as 'Updated: …'.
badgeReact.ReactNodePill rendered next to the widget title.
badgeReact.ReactNodePill rendered next to the widget title.
defaultCollapsedboolean"false"Start with the body collapsed to just the header.
defaultCollapsedboolean"false"Start with the body collapsed to just the header.
hiddenboolean"false"Initial visibility. Hidden widgets appear in the Add Widget panel.
hiddenboolean"false"Initial visibility. Hidden widgets appear in the Add Widget panel.
canDragboolean"true"Allow the user to drag this widget.
canDragboolean"true"Allow the user to drag this widget.
canResizeboolean"true"Allow the user to resize this widget.
canResizeboolean"true"Allow the user to resize this widget.
canRemoveboolean"true"Allow the user to hide this widget.
canRemoveboolean"true"Allow the user to hide this widget.
canCollapseboolean"true"Allow the user to collapse this widget.
canCollapseboolean"true"Allow the user to collapse this widget.
canMaximizeboolean"true"Allow the user to maximize this widget.
canMaximizeboolean"true"Allow the user to maximize this widget.
childrenreqReact.ReactNodeWidget body content.
childrenreqReact.ReactNodeWidget body content.
Features
Drag to Rearrange
Pick up any widget by its header in edit mode and drop it anywhere on the grid.
Resize Handles
Drag the corners to resize each widget. Uses the same Resizable primitive shipped in fluxo-ui.
Per-breakpoint Layouts
Separate layouts for lg/md/sm/xs. Auto-inherits and scales when a breakpoint has no explicit layout.
Show / Hide Widgets
Toolbar opens an "Add Widget" panel listing all hidden widgets. Re-add any one with a single click.
Maximize / Collapse
Maximize any widget to fill the dashboard for focused viewing, or collapse it to just the header. Collapsed widgets compact vertically so the row below floats up.
Custom Header Controls
Inject filter toggles, dropdowns, export buttons, share links, anything via headerActions — pointer events inside don’t start a drag.
Per-widget Settings
Render a settings popover via renderSettings(ctx). Built-in close + Escape + scroll/resize tracking.
Refresh + Loading
onRefresh returns a Promise — the icon spins until it resolves. Loading prop forces the overlay.
Presets
Named layout/visibility presets in the toolbar — "Sales focus", "Operations", etc.
Reset & Persistence
Pass persistKey for automatic localStorage save/restore. Toolbar Reset returns to defaults.
Keyboard Rearrangement
In edit mode, focus a widget header and use arrow keys to move it on the grid.
Chrome Variants
card / borderless / sunken / plain — pick what matches your dashboard aesthetic.
Themed & Dark Mode
Everything uses --eui-* tokens — flips automatically across all 12 brand themes and dark mode.