Muuri Draggable Dashboard Widgets — Live Reflow Snippet

Muuri Draggable Dashboard Widgets · Dashboards · Plain HTML, CSS & JS · Live preview

What's included

Features

Live drag reflow
dragSortHeuristics continuously repositions other widgets while a drag is in progress, not just at drop.
Configurable sort interval
sortInterval tunes how often the background re-sort check runs during a drag.
Distance-gated drag start
dragStartPredicate distance prevents plain clicks from being misread as zero-distance drags.
Mixed widget sizes
Quarter- and half-width widgets pack correctly with no manual column-span bookkeeping.
Automatic gap-filling
fillGaps: true keeps the layout tight as differently-sized widgets are rearranged.
Drag-state z-index layering
muuri-item-dragging and muuri-item-releasing classes keep the active widget visually on top.
Randomized bar sparklines
Each widget renders a small procedurally-heighted bar chart for visual texture.
No build step
Runs from a single CDN script tag with plain HTML/CSS/JS.

About this UI Snippet

Muuri Draggable Dashboard Widgets — Reflow While Dragging, Not Just at Drop

Screenshot of the Muuri Draggable Dashboard Widgets snippet rendered live

A lot of drag-and-drop libraries only tell you the final result: you drop an item, and *then* the rest of the list reorders. That works fine for a simple list, but it feels wrong for a dashboard of differently-sized widgets, where you want to see, in real time, where a widget will land as you drag it around — the same way OS-level window snapping previews a layout before you release the mouse.

dragEnabled and continuous re-sorting

new Muuri('#mdwGrid', { dragEnabled: true, ... })

Turning on dragEnabled does more than let you pick an item up — it activates Muuri's drag-sort heuristics, a background process that, while a drag is active, repeatedly checks which grid position the dragged item is currently closest to and re-triggers a full layout pass with the other items shifted to make room, *before* you let go. That's the mechanical difference from a drop-only reorder library: the other widgets are animating and repositioning live, frame by frame, as the cursor moves, not just once at the end.

dragSortHeuristics: { sortInterval: 40 } controls how often (in milliseconds) that background check runs. A smaller interval makes the reflow feel more immediately responsive to cursor movement but costs more layout recalculation; 40ms here is a reasonable middle ground — fast enough to feel live, not so fast it's calculating on every single mousemove.

dragStartPredicate and accidental-drag prevention

dragStartPredicate: { distance: 4 } requires the pointer to move at least 4 pixels before Muuri commits to starting a drag. Without this, a plain click (mousedown followed immediately by mouseup in roughly the same spot) could register as a zero-distance drag, which is a common source of "I just wanted to click the widget and it jittered" bugs. The distance threshold makes clicks and drags unambiguous.

Mixed widget sizes and the packing algorithm

Widgets here come in two widths — .w1 at 25% and .w2 at 50% — with independently varying heights set inline. Because Muuri's layout algorithm (with fillGaps: true) packs by finding the first available position that fits each item's actual measured dimensions, mixed sizes pack correctly without any manual grid-span bookkeeping: you don't declare "this widget spans 2 columns," you just make the DOM element wider, and Muuri measures it and packs around that.

z-index layering during drag

The stylesheet bumps z-index on .muuri-item-dragging (the item currently held) and .muuri-item-releasing (an item still animating into its settled position after drop) — both are classes Muuri toggles automatically. Without this, a dragged widget can visually disappear behind others mid-drag since normal DOM order/stacking doesn't account for which element the user is actively moving.

Reusing it

Swap the fixed .w1/.w2 width classes for a size picker so users can resize widgets themselves — Muuri repacks around a size change the same way it repacks around a drag, since both are just "an item's dimensions changed, recompute layout." Pair this with the filterable masonry grid if the dashboard also needs category filtering on top of manual rearrangement.

Build with AI

Build, Understand, Optimize, and Extend It With AI

The distinctive behavior here is live reflow during a drag rather than only at drop, so that's the best thing to probe with an AI assistant like Claude — ask it to trace through what dragSortHeuristics actually does on each tick and why a lower sortInterval trades responsiveness for computation cost. Then ask what would break (or feel worse) if dragEnabled were true but dragSortHeuristics were disabled entirely — the grid would still let you drag, but nothing would preview until drop. Good extensions to try: add a resize handle on each widget that toggles its width class and calls grid.refreshItems().layout() to repack, persist widget order to localStorage keyed by a stable id on each item, or add a "reset layout" button that restores the original widget order and sizes. Pair with the filterable masonry grid to combine live drag with category filtering on the same grid.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a draggable, live-reflowing dashboard of dashboard widgets using Muuri (v0.9, from a CDN) in plain HTML, CSS, and JavaScript.

Requirements:
- Generate 6 widgets from a JS array, each a card with a title, a colored status dot, a large metric number, a percentage delta line (colored green for improvement, red for decline), and a small procedurally-generated bar sparkline (random bar heights). Give widgets two different widths (e.g. 25% and 50% of the grid) and varying heights so the grid is a genuine mixed-size masonry, not uniform tiles.
- Initialize Muuri on the grid container with dragEnabled: true, a dragStartPredicate with a distance threshold (so a plain click isn't misread as a drag), layoutDuration/layoutEasing for the settle animation, dragSortHeuristics with a sortInterval controlling how often the live drag-sort recalculation runs, and layout: { fillGaps: true }.
- The key behavior to get right: while a widget is being actively dragged (before the user releases it), the OTHER widgets must visibly shift and animate into a previewed new arrangement in real time, not just snap into place after the drop. This is what dragEnabled plus the drag-sort heuristics produce — make sure the explanation content for this snippet is clear about why this differs from a library that only reorders on drop.
- Style muuri-item-dragging and muuri-item-releasing (classes Muuri applies automatically) with elevated z-index so the actively dragged widget stays visually on top of others during the drag.
- Style it as a dark analytics dashboard: rounded card widgets with a subtle border and shadow, a colored accent dot per widget, and a responsive absolutely-positioned grid container that Muuri manages entirely (no CSS grid-template).
- Keep all JavaScript in var/function style, no ES modules.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

Requires
<div class="mdw-stage">
  <div class="mdw-head">
    <span class="mdw-tag">Muuri · dragEnabled</span>
    <h2>Widget Dashboard</h2>
    <p>Drag any widget — the rest of the grid reflows live around it as you move, not just at drop.</p>
  </div>
  <div class="mdw-grid" id="mdwGrid"></div>
</div>

Step by step

How to Use

  1. 1
    Add the Muuri CDNInclude muuri.min.js from the CDN panel — a single script tag, no build step.
  2. 2
    Paste HTML, CSS, and JSSix mixed-size widgets (quarter and half-width) build themselves from a JS array.
  3. 3
    Drag any widgetdragEnabled: true lets you pick up a widget by clicking and moving at least 4px.
  4. 4
    Watch the live reflowdragSortHeuristics re-checks every 40ms so other widgets shift before you release.
  5. 5
    Drop it anywhereThe widget settles into the position it was previewing, animated by layoutDuration.
  6. 6
    Resize widgets in codeChange a widget's width class or height — Muuri repacks around the new dimensions automatically.

Real-world uses

Common Use Cases

Analytics dashboards
Let users rearrange KPI widgets to match their own priorities, next to a filterable grid.
Admin control panels
Draggable panels for logs, metrics, and alerts that users can personalize.
Customizable home screens
A widget-based landing dashboard, similar to mobile OS home screen editing.
Internal tools
Give teams control over which metrics are most visible without redeploying code.
Teaching live-reflow drag
A concrete reference for drag-sort heuristics versus simple drop-only reordering.

Got questions?

Frequently Asked Questions

Enabling dragEnabled activates Muuri's drag-sort heuristics: a background process that repeatedly checks, at the interval set by dragSortHeuristics.sortInterval, which grid slot the dragged item is currently nearest to, and triggers a live layout pass with the other widgets shifted to preview that arrangement — all before the pointer is released.

It's the number of milliseconds between each background re-sort check during an active drag. A lower value makes the reflow track the cursor more immediately at the cost of more frequent layout recalculation; a higher value is cheaper but feels laggier. 40ms is a reasonable default for a dashboard-sized grid.

Without it, Muuri can interpret a plain click (a mousedown immediately followed by a mouseup at nearly the same coordinates) as a very short drag, causing an unwanted jitter. Requiring 4px of pointer movement before a drag officially begins keeps ordinary clicks unambiguous from drag gestures.

Muuri measures each item's actual rendered width and height and packs items into the tightest available position that fits those measured dimensions — there is no declarative "spans 2 columns" concept to keep in sync. Making a widget's CSS width 50% instead of 25% is sufficient; Muuri's layout algorithm adapts around it automatically.

Muuri toggles these classes automatically but leaves styling to you. Without a higher z-index, the widget currently being dragged (or still animating into its settled position right after release) can end up visually behind other absolutely-positioned items due to normal DOM stacking order, which looks broken during a drag.

Yes — Muuri repacks the grid whenever any item's measured dimensions change, whether that change comes from a drag or from you toggling a widget's width/height class. A resize handle that swaps a widget between w1 and w2 classes and then calls grid.refreshItems().layout() would trigger the same repack logic already driving drag reflow.