Examples
Photo Gallery
A polished image-gallery pattern using known aspect ratios.
Image galleries feel best when the layout can reserve space before media loads. Use known width and height values to compute each rendered item height.
Marfa, TX
Desert dawn
Seattle, WA
Glass house
Reykjavik, IS
Blue hour
Lisbon, PT
Courtyard
Brooklyn, NY
Studio wall
Kyoto, JP
Quiet arch
Busan, KR
Sea path
Portland, OR
Green room
Example
import { clsx } from 'clsx';
import { MasonryBalanced } from 'masonix';
type Photo = {
id: string;
title: string;
location: string;
gradient: string;
width: number;
height: number;
};
function PhotoCard({ photo, width }: { photo: Photo; width: number }) {
const renderedHeight = Math.round(width * (photo.height / photo.width));
return (
<article
className={clsx(
'relative overflow-hidden',
'rounded-xl border',
'border-white/20',
)}
style={{ height: renderedHeight, background: photo.gradient }}
>
<div className="absolute inset-0 bg-gradient-to-b from-black/5 via-transparent to-black/60" />
<div className="absolute inset-x-0 bottom-0 p-4">
<p className="text-xs font-medium uppercase tracking-wide text-white/65">
{photo.location}
</p>
<h3 className="mt-1 text-base font-semibold text-white">
{photo.title}
</h3>
</div>
</article>
);
}
export function PhotoGallery({ photos }: { photos: Photo[] }) {
return (
<MasonryBalanced
items={photos}
columns={{ 0: 1, 560: 2, 860: 3 }}
gap={14}
defaultWidth={960}
itemKey={(photo) => photo.id}
getItemHeight={(photo, _itemIndex, columnWidth) =>
Math.round(columnWidth * (photo.height / photo.width))
}
render={({ data, width }) => <PhotoCard photo={data} width={width} />}
/>
);
}When to use
Use this when the API already gives image dimensions. It works well for portfolio pages, editorial galleries, and commerce lookbooks.
Notes
- Known heights reduce layout shift.
defaultWidthimproves the first server-rendered layout.- Keep
alttext on the actual image, not on the wrapper.