Projection
projectLayout(layout, targetCanvas, options) returns a new layout whose items keep their relationships (rows, columns, alignment, fixed sizes) while filling the target canvas. It is the function that makes a pixel layout responsive.
targetCanvas is a Partial<GridCanvas>; missing fields come from the source canvas. Two engines are available through options.strategy.
Chain (default)
Items that overlap on the perpendicular axis form a chain: items sharing vertical extent form a horizontal chain, items sharing horizontal extent a vertical one. Within a chain:
- fixed-axis items keep their pixel size;
- gaps equal to
options.gap(within 2px, as are touching edges) stay exactly that size; - empty space scales with the canvas;
- free items absorb the remainder proportionally to their authored size.
A second pass restores configured gaps, anchors fixed items that touch a canvas edge to that edge, and snaps edges that were identical in the source to identical values in the target, so aligned items stay aligned after rounding.
The chain engine is what nested layouts use to render each container, and what GridProvider uses on every measurement. It costs roughly two to three times the segment engine on the same input (see performance).
Lower-level pieces are exported for custom pipelines: scaleItems (fractional flex projection), preserveGaps (the second pass; mutates in place), projectItemsByChain (both together), projectFloatingRect (ratio-scale a rect that is not part of any chain), roundItemRects, and syncFixedDimensions.
Segments
Every distinct item edge along an axis becomes a stop. The segments between stops are either fixed (empty, or covered only by fixed-size items) or flexible. Flexible segments scale to absorb the difference between the source and target span; fixed segments keep their pixels. A normalization pass then resolves any overlaps that per-item clamping introduced.
Segments are simpler, cheaper, and work well for sparse layouts where items do not form obvious rows and columns. They do not take a gap option; authored spacing scales with everything else unless it sits between fixed items. The engine also writes projected minW/maxW/minH/maxH onto each item so constraints scale with the canvas.
Properties you can rely on
- Identity at the same size. Projecting a layout onto a canvas equal to its own returns the same geometry (clamped to bounds). This is what lets a committed rendered layout become the next source without drift.
- Padding is preserved in pixels. Both engines work inside the padded area and translate back.
- Locked items are respected by the chain engine when redistributing space; they are still scaled when the whole chain must fit.
- Scrollable canvases grow. Projection alone does not change
height; callfitCanvasToContent(canvas, items)afterwards (the React provider does) to extend a scrollable canvas below its last item.
Choosing a target size
Measure the element and round to whole pixels. Projecting on every ResizeObserver callback is fine at typical item counts: 128 items project in about 1.6ms with chains and 1ms with segments on a desktop CPU. For very large layouts, throttle to animation frames.