Skip to content

Photo viewer

Source: artifex/frontend/src/components/PhotoViewer.jsx Category: Composite component

Photo viewer — Artifex’s fullscreen image view. Two-row toolbar (actions on top, zoom/info on the bottom), an image canvas with pan + zoom, and an optional metadata sidebar showing prompt/model/sampler/seed. Keyboard-driven: arrows navigate, +/- zoom, I toggles info.

A right-hand-side panel (or fullscreen overlay on mobile) that opens when a user clicks a gallery thumbnail. Shows one image at a time with quick actions (favorite, add-to-collection, share, download, delete). The info sidebar reads from the image’s normalized workflow JSON data.

A gallery without a detail view is a gallery you can’t use. The viewer exists to answer “what is this image” — dimensions, generation params, tags, comments — and to let you act on it without leaving the flow.

Two-row toolbar pattern: primary actions (favorite, share, delete) on top; viewport actions (zoom, info) on the bottom. Separating them prevents the “where was that button” problem in a busy toolbar.

moonlit-forest.png3 of 42
100%
2048×2048 · 3.2 MB · PNG

The demo’s image is a CSS-gradient square; real Artifex renders the image itself (with a progressive-loading placeholder).

  • Artifex — opened from any gallery click; URL-routable (/photo/:id)
  • Pattern generalizes to any image-viewer app (not limited to AI galleries)
  • Zoom applied as CSS transform: scale(), not width/height. Transform is GPU-accelerated and smooth; width/height triggers layout on every pixel of change.
  • Pan needs pointer capture. Without element.setPointerCapture(e.pointerId) on pointerdown, a drag that leaves the element stops receiving move events mid-stroke.
  • Preloading next/prev. On arrow-key nav, the next image needs to already be loaded. Inject <link rel="prefetch"> for the next/prev preview URL when the viewer opens.
  • Toolbar button states. “Favorite” should flip optimistically and revert on API failure. Otherwise every click feels laggy waiting for the round trip.
  • Delete confirmation. Single-key delete is a user-hostile default. Require confirmation, or a hold-to-delete with a progress indicator.
  • Info sidebar doesn’t always fit. On mobile, overlay the info as a bottom sheet instead of a sidebar. Single component handling both responsive modes is more work than two components sharing the state.
  • Keyboard shortcuts conflict with browser defaults. Cmd+F for favorite conflicts with find; Cmd+Shift+F is better but unintuitive. Document the shortcuts visibly.
  • Mid-zoom resize. Window resize during zoom shouldn’t reset the zoom level, only re-center the image.
  • Metadata is optional. Not every image has workflow JSON (imported files, screenshots). Render a “no generation data” state gracefully.