
I started this as a performance task: downscale assets, add srcSet/sizes, and improve LCP. The bigger problem turned out to be maintainability.
Image behavior was spread across scripts and components with repeated conventions:
- variant naming assumptions,
- hardcoded
srcSetstrings, - multiple script aliases and legacy paths.
That made drift easy, and small changes in one place could quietly break expectations elsewhere. I replaced that with a simpler pattern: generate one manifest at build time (src/generated/imageVariantManifest.json) and resolve variants from it at runtime.
What got simpler
Three things became simpler:
- One canonical workflow:
yarn image:variants
- Less duplicated rendering logic:
- shared
ResponsiveImagecomponent instead of repeated<picture>patterns
- shared
- Static assets became data-driven:
- background and portrait
srcSetvalues now come from manifest-backed metadata, not JSX literals
- background and portrait
Human constraint: it should just work
This was an important constraint for me: I do not want to manually create -sm, -md, -lg files every time I add an image.
The workflow is now:
- Add the source image.
- Reference it in frontmatter or markdown.
- Run
yarn image:variants.
That keeps authoring simple while still making outputs consistent.
Reliability and Performance as Outcomes
Once maintainability improved, reliability and performance improved with it. I also removed silent fallback for required profiles (cover.card, cover.hero, inlineContent). If a required variant is missing, it now fails fast instead of shipping a hidden regression.
Takeaway
I started by chasing LCP. The durable fix was maintainability: fewer scattered conventions, one source of truth, and clearer build/runtime boundaries. For this site, performance improvements became much easier once the image system became easier to reason about.
Related posts
From Writing Code to Orchestrating Agents
February 3, 2026
How my AI workflow changed from quick snippets to a practical plan/implement/verify loop.
The 'Boring' Architecture Behind This Blog
January 31, 2026
How I added a fully static, markdown-based blog to my Next.js portfolio.
Everyone Is a Builder Now
February 28, 2026
Over cocktails, my colleagues and I talked about why AI lowering the barrier to building is a net positive.
