Before Photos Reach the Site: Why I Needed an R2 Image Pipeline
The real requirement was not just uploading images. It was turning local originals into reliable web assets with sizes, URLs, metadata, and repeatable content data.
Yesterday I added a small photo pipeline for the gallery: source images go into photos/raw, scripts produce WebP variants, the assets are uploaded to Cloudflare R2, and YAML files are generated for content/gallery.
At first glance this sounds like "upload some photos." The actual requirement had three parts.
Originals Are Not Web Assets
Camera and phone originals are too large for direct page rendering. A single photo can be several megabytes, sometimes much more. Serving those files in a gallery would hurt first load, bandwidth, and mobile browsing.
So the originals stay out of the repository, and the public pages never point at them directly. The repo stores content metadata; R2 stores the actual image assets.
One Photo Has Multiple Jobs
The gallery has at least three viewing contexts:
- The grid needs a small image that appears quickly.
- The lightbox needs a balanced display image.
- The original-view action needs a larger file with more detail.
That is why the script emits thumb.webp, display.webp, and full.webp. The three variants map to real user paths instead of treating every visitor as if they need the largest file immediately.
Content Data Should Be Repeatable
After upload, the site still needs title, dimensions, capture date, camera metadata, URLs, and a blur placeholder. Filling those fields by hand would be slow and fragile.
The processing script writes a manifest, and the generation step converts that manifest into YAML. The Next.js pages only read YAML; they do not need to know where the local file lived or how it reached R2.
Takeaway
The useful part of the pipeline is not saving one manual upload. It turns loose photo files into reliable web assets: sized, addressable, metadata-rich, and easy for the content system to consume again.

