Skip to Content
snapgrid is a react-grid-layout v2 alternative built on dnd-kit. Drag, resize, repack, and drag between grids.
DocumentationGuidesCompaction & packing

Compaction & packing

After every move, resize, insert, or remove, the layout is re-packed by a Compactor. Pass one to the compactor prop; swap it any time. It’s just a value.

import { GridLayout, verticalCompactor, horizontalCompactor, noCompactor } from "@snapgridjs/react"; <GridLayout compactor={horizontalCompactor} /* … */ />;
Compaction
swap the packing algorithm, then drag

Built-in compactors

Exported from @snapgridjs/react (re-exported from the core engine):

CompactorBehaviour
verticalCompactorItems fall upward to fill gaps. The default, matches react-grid-layout.
horizontalCompactorItems pack to the left.
noCompactorFree positioning: items stay where dropped and may overlap.

These honor static items and per-item collision rules.

Extra packers

@snapgridjs/extras adds variable-height packing styles. They repack all items by reading order, so the dropped cell influences order while the algorithm decides final positions.

import { masonryCompactor, gravityCompactor, shelfCompactor } from "@snapgridjs/extras";
CompactorBehaviour
masonryCompactorMinimizes height by dropping each item into its shortest column span.
gravityCompactorTop-left gravity: each item falls into the earliest free hole (row-major).
shelfCompactorPacks left-to-right into rows, wrapping to a new shelf when full.

The extra packers repack everything and do not preserve static placement or enforce maxRows (the Compactor.compact(layout, cols) contract only receives the column count). Use a built-in compactor when you need static tiles or a hard row cap.

Fast compactors

@snapgridjs/extras also re-exports react-grid-layout’s O(n log n) vertical and horizontal compactors: drop-in replacements for the built-in cascades that produce the same packing on clean (non-overlapping) layouts but pack far faster at scale.

import { fastVerticalCompactor, fastHorizontalCompactor } from "@snapgridjs/extras"; <GridLayout compactor={fastVerticalCompactor} /* … */ />;

They only help with bulk compaction: building a large layout from scratch, a responsive reflow, or compacting imported data. Dragging is already cheap (each move re-packs an already-tidy layout), so the fast variants don’t change how a drag feels; reach for them when you pack hundreds to thousands of items at once.

react-grid-layout’s published benchmarks, the standard O(n²) cascade vs. the fast O(n log n) “rising tide”:

ItemsStandard verticalFast verticalSpeedup
50112 µs19 µs
100203 µs36 µs
200821 µs51 µs16×
5005.7 ms129 µs45×
ItemsStandard horizontalFast horizontalSpeedup
50164 µs12 µs14×
100477 µs25 µs19×
2001.1 ms42 µs26×

The fast compactors are the “rising tide” algorithm by @morris  (react-grid-layout #2152 ). Keep the built-in verticalCompactor / horizontalCompactor as your default. They’re the exact-parity path that honors static and maxRows; the fast variants are an opt-in for very large grids.

Writing a custom compactor

A Compactor is a small object. Implement compact(layout, cols) and return the packed layout:

import type { Compactor, Layout } from "@snapgridjs/react"; const diagonalCompactor: Compactor = { type: null, // null = free-positioning from the engine's perspective allowOverlap: false, compact(layout: Layout, cols: number): Layout { return [...layout] .sort((a, b) => a.y - b.y || a.x - b.x) .map((item, idx) => ({ ...item, x: Math.min(idx, cols - item.w), y: idx })); }, };
FieldDescription
type"vertical" | "horizontal" | "wrap" | null. Built-in types use the engine’s native cascade; null means your compact() does the work.
compact(layout, cols)Returns the re-packed layout. Called after every interaction.
allowOverlapIf true, items may overlap and the result of the move is returned as-is.
preventCollisionIf true, moves/resizes into occupied cells are rejected instead of cascading.
Last updated on