Fluxo UIFluxo UIv0.4.93

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.

Sales
Today
$12,480+18%
Orders
284
Avg ticket
$43.94
Visitors
4,231+12%
Unique visitors last 24h
Recent Orders
Alex Chen
#10245
$248.00
shipped
Priya Patel
#10244
$72.50
paid
Sara Okafor
#10243
$1,210.00
paid
Diego Romero
#10242
$96.25
pending
Team
AC
Alex Chen
Engineering
PP
Priya Patel
Design
SO
Sara Okafor
Product
DR
Diego Romero
Sales
System Health
CPU32%
Memory68%
Disk45%
Network18%
Channels
Direct42%
Organic28%
Referral18%
Social12%
Calendar
S
M
T
W
T
F
S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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.

Sales
Today
$12,480+18%
Orders
284
Avg ticket
$43.94
Visitors
4,231+12%
Unique visitors last 24h
Recent Orders
Alex Chen
#10245
$248.00
shipped
Priya Patel
#10244
$72.50
paid
Sara Okafor
#10243
$1,210.00
paid
Diego Romero
#10242
$96.25
pending
Team
AC
Alex Chen
Engineering
PP
Priya Patel
Design
SO
Sara Okafor
Product
DR
Diego Romero
Sales
System Health
CPU32%
Memory68%
Disk45%
Network18%
Channels
Direct42%
Organic28%
Referral18%
Social12%
Calendar
S
M
T
W
T
F
S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Saved to 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.

Sales
Today
$12,480+18%
Orders
284
Avg ticket
$43.94
Visitors
4,231+12%
Unique visitors last 24h
Recent Orders
Alex Chen
#10245
$248.00
shipped
Priya Patel
#10244
$72.50
paid
Sara Okafor
#10243
$1,210.00
paid
Diego Romero
#10242
$96.25
pending
Team
AC
Alex Chen
Engineering
PP
Priya Patel
Design
SO
Sara Okafor
Product
DR
Diego Romero
Sales
System Health
CPU32%
Memory68%
Disk45%
Network18%
Channels
Direct42%
Organic28%
Referral18%
Social12%
Calendar
S
M
T
W
T
F
S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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.

Card chrome
card
Borderless
borderless
Sunken
sunken
Plain
plain (no chrome)
With badgeNEW
Pill rendered next to the title
With error
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.

Sales
$12,480
Filter: off
Visitors
4,231
Range: 7 days
Recent Orders
Try collapsing one of the widgets above — the widget below floats up to fill the freed row.
Tip: wrap interactive controls in 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.

Sales
Today
$12,480+18%
Orders
284
Avg ticket
$43.94
Visitors
4,231+12%
Unique visitors last 24h
Recent Orders
Alex Chen
#10245
$248.00
shipped
Priya Patel
#10244
$72.50
paid
Sara Okafor
#10243
$1,210.00
paid
Diego Romero
#10242
$96.25
pending
Team
AC
Alex Chen
Engineering
PP
Priya Patel
Design
SO
Sara Okafor
Product
DR
Diego Romero
Sales
System Health
CPU32%
Memory68%
Disk45%
Network18%
Channels
Direct42%
Organic28%
Referral18%
Social12%
Calendar
S
M
T
W
T
F
S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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

widgetsreq
DashboardWidget[]

All widgets that can appear on the dashboard. Hidden ones are listed in the Add Widget panel.

layouts
DashboardLayouts

Controlled per-breakpoint layouts: { lg: WidgetLayout[], md: ..., ... }. Pair with onLayoutChange.

defaultLayouts
DashboardLayouts

Initial per-breakpoint layouts (uncontrolled). Auto-generated if omitted.

onLayoutChange
(payload: DashboardLayoutChangePayload) => void

Fires on every layout, visibility, collapse, maximize, preset or reset change.

breakpoints
DashboardBreakpoint[]"xl/lg/md/sm/xs"

Custom breakpoint definitions: { key, minWidth, columns, label? }.

rowHeight
number"64"

Pixel height of one grid row.

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.

minWidgetWidth
number"1"

Minimum column span for any widget.

minWidgetHeight
number"1"

Minimum row span for any widget.

editMode
boolean

Controlled edit mode. When true, drag/resize handles and toolbar actions are enabled.

defaultEditMode
boolean"false"

Initial edit mode (uncontrolled).

onEditModeChange
(editing: boolean) => void

Fires when the user toggles edit mode.

persistKey
string

localStorage key for automatic save/restore of the full DashboardLayoutState.

presets
DashboardLayoutPreset[]

Named layout presets to expose in the toolbar Presets menu.

onPresetChange
(preset: DashboardLayoutPreset | null) => void

Fires when the user selects a preset.

showToolbar
boolean"true"

Show the built-in toolbar (Edit, Add Widget, Reset, Presets).

toolbarTitle
string

Title rendered on the left of the toolbar (also used as the grid's aria-label).

toolbarSlotStart
React.ReactNode

Custom content rendered after the toolbar title.

toolbarSlotEnd
React.ReactNode

Custom content rendered before the Edit button on the right side of the toolbar.

addWidgetLabel
string

Override the Add Widget button label.

compactType
'vertical' | 'horizontal' | null"'vertical'"

Auto-compaction direction after drag/resize. null disables compaction.

allowOverlap
boolean"false"

Allow widgets to overlap (skip collision push-down).

emptyState
React.ReactNode

Custom empty state when there are no visible widgets.

locale
Partial<DashboardLayoutLocale>

Override any built-in toolbar / action label.

className
string

Class name applied to the outer wrapper.

style
React.CSSProperties

Inline styles for the outer wrapper.

widgetClassName
string

Class name applied to every widget surface.

DashboardWidget Props

idreq
string

Unique widget id; referenced by layouts and presets.

titlereq
string

Widget title shown in the header bar and Add Widget panel.

icon
SVGIcon

Optional header icon (from fluxo-ui/icons).

description
string

Short description shown in the Add Widget panel.

category
string

Optional grouping label for future categorization.

chrome
'card' | 'plain' | 'borderless' | 'sunken'"'card'"

Visual chrome around the widget.

headerActions
React.ReactNode

Custom 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.ReactNode

Render-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.

loading
boolean

Force the loading overlay independent of refresh state.

error
string | null

Display an error block in place of the body content.

lastUpdated
Date | string | number

Shown in the widget footer as 'Updated: …'.

badge
React.ReactNode

Pill rendered next to the widget title.

defaultCollapsed
boolean"false"

Start with the body collapsed to just the header.

hidden
boolean"false"

Initial visibility. Hidden widgets appear in the Add Widget panel.

canDrag
boolean"true"

Allow the user to drag this widget.

canResize
boolean"true"

Allow the user to resize this widget.

canRemove
boolean"true"

Allow the user to hide this widget.

canCollapse
boolean"true"

Allow the user to collapse this widget.

canMaximize
boolean"true"

Allow the user to maximize this widget.

childrenreq
React.ReactNode

Widget 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.