MasonryGrid Component
Renders one measured wrapper <div> per item around that item's #item slot — item content stays entirely yours, this only measures and positions the wrapper. With options.virtualize on, only the currently visible items (± overscan) actually get a wrapper/slot at all — see Virtualization.
<MasonryGrid :items="items" :options="{ columns: 'auto', minLaneSize: 240 }">
<template #item="{ item }">
<MyCard :data="item" />
</template>
</MasonryGrid>Props
items
MasonryGridItem[] · required
Same shape as core's MasonryItemDescriptor (see Items & Bento Spans), minus el — the component creates and owns each item's wrapper element itself. Every field is optional, including id: id, colSpan, rowSpan, aspectRatio, estimatedSize, order.
If id is omitted, the component generates a stable one keyed by the item object's own reference (a WeakMap) — the same object in the array keeps the same id across any re-render. If items gets rebuilt into fresh objects on every render instead (e.g. .map()-ing over source data each time), each fresh object gets a newly generated id — give those an explicit id from your own data instead.
options
MasonryOptions · default: {}
Passed straight through to createMasonryEngine() — see Engine API. Core has no live updateOptions(), so a changed options content (compared structurally via optionsEqual, not just by reference) tears down and recreates the engine — an inline object literal that happens to have identical content on every render doesn't trigger this.
sortable
boolean · default: false
Turns every item wrapper into a keyboard-reorderable item — see Sortable below.
Emits
layout
Payload: MasonryItemLayout[]
Fired on every engine relayout, with every item's resolved box.
reorder
Payload: MasonryGridItem[]
Fired when the user reorders an item via keyboard (sortable only) — see Sortable below. Reordering never mutates items itself.
Slots
item
Scope: { item: MasonryGridItem }
The one slot rendered once per entry in items (or once per currently-visible entry, under virtualize) — the item's own content, wrapped in a measured <div class="mk-item"> the engine positions.
Sortable
<script setup lang="ts">
import { ref } from 'vue'
import { MasonryGrid } from '@macrulez/masonry-kit-vue'
const items = ref([{ id: 'a' }, { id: 'b' }, { id: 'c' }])
</script>
<template>
<MasonryGrid :items="items" sortable @reorder="items = $event">
<template #item="{ item }">
<MyCard :data="item" />
</template>
</MasonryGrid>
</template>With sortable on, every item wrapper becomes keyboard-operable:
- Space or Enter picks up the focused item (calls
engine.setDragging(id)— see Engine API — so it's excluded from normal packing while active) or, if it's already picked up, drops it. - Arrow keys (Down/Up at
direction: 'vertical', Right/Left at'horizontal') move the picked-up item one position forward or backward initems, emittingreorderwith the new array on every step. - Escape cancels — restores
itemsto the array captured at pickup time (by emittingreorderwith that snapshot) and releases the drag.
<MasonryGrid> never mutates items on its own — every move is a reorder emit with a brand-new array; your own v-model-style binding (@reorder="items = $event") is what actually applies it, the same way any other controlled-list pattern works.
Each wrapper gets role="button", aria-roledescription="Reorderable item", aria-pressed (reflecting pickup state), and aria-describedby pointing at a visually-hidden instructions string ("Press space or enter to pick up this item…"). Every pickup/move/drop/cancel is also announced through a visually-hidden aria-live="polite" region.
Pointer/touch drag is deliberately not implemented — see Overview for why a live pointer-drag reflow doesn't fit skyline packing the way a discrete keyboard step does.