Get image dimensions from the API, use a virtualized grid, serve small responsive thumbnails from an image CDN, and keep the lightbox in the URL.
Requirements:
- A grid of photos, uniform or masonry (columns of images with different heights, like Pinterest).
- Infinite scroll.
- Open a photo in a lightbox (a full-screen viewer) with next and previous.
- Zoom and keyboard support.
Non-functional: smooth scroll with thousands of images, minimal layout shift, and low data use on mobile.
Architecture: Gallery holds a virtualized grid of Thumbnails. Virtualized means only visible thumbnails are in the DOM. Lightbox is a route or a URL param (?photo=123). So it is shareable, and the back button closes it.
Data and API: a cursor-paginated GET /photos. It returns the id, width, height, a dominant colour or blur hash, and URLs for several sizes from an image CDN (content delivery network).
Optimisations:
- Dimensions from the API let the layout compute positions before images load. This works for justified rows or masonry columns. So there is no layout shift, and virtualization can work with known heights.
- Thumbnails use
srcsetwith small widths, in AVIF or WebP, and lazy loading. - The lightbox loads a large size and preloads the neighbouring photos.
- Add
decoding="async"to images. It is only a hint to the browser: decoding this image should not delay showing other content. - Placeholders show a colour immediately.
Accessibility: alt text, a lightbox that traps focus, and arrow-key navigation.
Trade-offs: masonry looks better, but it makes virtualization and reading order harder.
- Return image dimensions in the API, so the layout is set before images load.
- Use a virtualized grid.
- Serve responsive thumbnails from an image CDN, with lazy loading.
- Keep the lightbox state in the URL, and preload neighbouring photos.
- Show placeholders, and make the lightbox accessible.