A responsive masonry image grid that opens a swipeable, keyboard-navigable lightbox. Tiles keep each image's ratio, captions show in the lightbox only, and the lightbox is a fullscreen `Modal`.
Examples of v0.1.6 are shown as code; only the latest version runs live. Open the live examples →
The default grid: two columns on a phone, three from 48rem and four from 64rem. Tap a tile to open the lightbox.
Basic Gallery
1const placeholder = (hue: number, width: number, height: number) =>2 `data:image/svg+xml;utf8,${encodeURIComponent(3 `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"><rect width="100%" height="100%" fill="hsl(${hue} 80% 55%)"/></svg>`,4 )}`;56const SIZES = [7 [1200, 800],8 [800, 1200],9 [1000, 1000],10 [1200, 800],11 [800, 1000],12 [1200, 900],13 [900, 1200],14 [1200, 800],15] as const;1617const IMAGES: GalleryImage[] = SIZES.map(([width, height], index) => ({18 src: placeholder(index * 45, width, height),19 alt: `Photo ${index + 1}`,20 width,21 height,22 caption: index % 2 === 0 ? `Caption for photo ${index + 1}` : undefined,23}));2425const BasicGallery = () => <Gallery images={IMAGES} />;2627render(<BasicGallery />);
A fixed number of columns at every width.
Three Columns
1const placeholder = (hue: number, width: number, height: number) =>2 `data:image/svg+xml;utf8,${encodeURIComponent(3 `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"><rect width="100%" height="100%" fill="hsl(${hue} 80% 55%)"/></svg>`,4 )}`;56const SIZES = [7 [1200, 800],8 [800, 1200],9 [1000, 1000],10 [1200, 800],11 [800, 1000],12 [1200, 900],13 [900, 1200],14 [1200, 800],15] as const;1617const IMAGES: GalleryImage[] = SIZES.map(([width, height], index) => ({18 src: placeholder(index * 45, width, height),19 alt: `Photo ${index + 1}`,20 width,21 height,22 caption: index % 2 === 0 ? `Caption for photo ${index + 1}` : undefined,23}));2425const ThreeColumns = () => <Gallery images={IMAGES} columns={3} />;2627render(<ThreeColumns />);
Columns per breakpoint: `xs` below 48rem, `md` from 48rem, `lg` from 64rem.
Responsive Columns
1const placeholder = (hue: number, width: number, height: number) =>2 `data:image/svg+xml;utf8,${encodeURIComponent(3 `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"><rect width="100%" height="100%" fill="hsl(${hue} 80% 55%)"/></svg>`,4 )}`;56const SIZES = [7 [1200, 800],8 [800, 1200],9 [1000, 1000],10 [1200, 800],11 [800, 1000],12 [1200, 900],13 [900, 1200],14 [1200, 800],15] as const;1617const IMAGES: GalleryImage[] = SIZES.map(([width, height], index) => ({18 src: placeholder(index * 45, width, height),19 alt: `Photo ${index + 1}`,20 width,21 height,22 caption: index % 2 === 0 ? `Caption for photo ${index + 1}` : undefined,23}));2425const ResponsiveColumns = () => (26 <Gallery images={IMAGES} columns={{ xs: 1, md: 2, lg: 5 }} />27);2829render(<ResponsiveColumns />);