WebberUI

Zoomable Density Grid

A density zoom grid — switch the number of columns per row the way iOS Photos does, with every cell reflowing smoothly through a FLIP animation.

Loading preview…
npx shadcn@latest add https://webberui.com/r/zoomable-density-grid.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

2
8
<ZoomableDensityGrid />

Installation

npx shadcn@latest add https://webberui.com/r/zoomable-density-grid.json

Or, once registries are configured in components.json, install it as @webberui/zoomable-density-grid.

Usage

import { ZoomableDensityGrid } from "@/components/ui/zoomable-density-grid";

<ZoomableDensityGrid densities={[2, 4, 6]} defaultDensity={4} gap={8}>
  {photos.map((src) => (
    <img
      key={src}
      src={src}
      alt=""
      className="aspect-square rounded-lg object-cover"
    />
  ))}
</ZoomableDensityGrid>

Every direct child is wrapped automatically in an animated motion.div cell, with no extra markup needed.

Props

PropTypeDefaultDescription
densitiesnumber[][2, 4, 6]Density steps (each step = columns per row), shown on the switcher in order
defaultDensitynumberdensities[0]Initial column count; must be one of the densities steps
gapnumber8Gap between cells (px)
childrenReact.ReactNodeGrid content; every direct child is wrapped automatically in an animated cell
onDensityChange(density: number) => voidFires when the density changes, receiving the new column count
classNamestringAppended to the outermost container's className

How it works

  • FLIP reflow: when the column count changes, every cell transitions its position and size continuously through Motion's layout animation rather than fading out and reappearing
  • Scale-down spring back: at the moment of the switch, every cell takes a brief scale-down and spring back (spring), mimicking the feel of zooming in iOS Photos
  • Sliding pill: the switcher's selected pill slides between steps through layoutId
  • The layout animation is implemented with transforms, so cell content scales briefly during the transition; flat color blocks, images, and similar content look best
  • The steps passed to densities are rounded, floored at 1, and deduplicated automatically; an empty array falls back to the default [2, 4, 6]

Accessibility

  • When the user has "reduce motion" enabled at the system level, the FLIP and the pill slide are disabled and a switch jumps straight to the new layout (the DOM structure matches SSR)
  • The switcher is a group of native <button>s (role="group" plus aria-label), each step marking its selected state with aria-pressed and carrying an aria-label of "N columns per row"
  • The steps can be focused in order with Tab, showing an outline on focus-visible

On this page