Masonry takes an array of items (each with an img and a pixel height) and lays them out into a responsive column grid, animating each tile in and reacting to hover.
Preview
Installation
$ npx shadcn@latest add https://spark-ui-olive.vercel.app/r/masonry.json
Usage
import { Masonry } from "@/components/masonry";const ITEMS = [
{ id: "1", img: "/images/trail-1.jpg", height: 400 },
{ id: "2", img: "/images/trail-2.jpg", height: 250 },
{ id: "3", img: "/images/trail-3.jpg", height: 600 },
{ id: "4", img: "/images/trail-4.jpg", height: 350 },
{ id: "5", img: "/images/trail-5.jpg", height: 300 },
{ id: "6", img: "/images/trail-1.jpg", height: 450 },
{ id: "7", img: "/images/trail-2.jpg", height: 320 },
{ id: "8", img: "/images/trail-3.jpg", height: 400 },
];
export default function MasonryDemo() {
return (
<Masonry
items={ITEMS}
className="max-w-5xl"
ease="power3.out"
duration={0.6}
stagger={0.05}
animateFrom="bottom"
scaleOnHover
hoverScale={0.95}
blurToFocus
colorShiftOnHover={false}
/>
);
}Examples
Cards
Pass renderItem to render custom content — like a title and description overlay — instead of the default image tile.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
items | { id: string; img: string; url?: string; height: number }[] | — | Items to arrange. `height` sets the tile's pixel height; `url` (optional) makes the tile a link. |
ease | Easing | "easeOut" | Framer Motion easing for the entry/hover transitions. Common gsap ease names (e.g. "power3.out") are mapped automatically. |
duration | number | 0.6 | Entry animation duration in seconds. |
stagger | number | 0.05 | Delay in seconds added per item, in layout order. |
animateFrom | "top" | "bottom" | "left" | "right" | "center" | "random" | "bottom" | Direction each tile animates in from on mount. |
scaleOnHover | boolean | true | Whether tiles scale on hover. |
hoverScale | number | 0.95 | Scale factor applied on hover when scaleOnHover is enabled. |
blurToFocus | boolean | true | Whether tiles start blurred and sharpen in as they animate on mount. |
colorShiftOnHover | boolean | false | Whether a color overlay fades in over a tile on hover. |
renderItem | (item: T, index: number) => ReactNode | — | Overrides the default image tile with custom content, while keeping the same layout, entry and hover animations. |
className | string | — | Additional classes for the grid container. |
Accessibility
- Tiles with a
urlrender as links (<a>); tiles without one are non-interactive. - Provide meaningful
imgsources; decorative tiles otherwise render with an emptyalt.