
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.
Simpler Operating Model
Three things became simpler. First, there is now one canonical workflow: yarn image:variants. Second, rendering logic is less duplicated because a shared ResponsiveImage component replaced repeated <picture> patterns. Third, static assets became data-driven, so background and portrait srcSet values now come from manifest-backed metadata rather than JSX literals.
Authoring 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 simple: add the source image, reference it in frontmatter or markdown, then run yarn image:variants. That keeps authoring lightweight 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.
Durable Lesson
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.
Get new posts by email
Subscribe for occasional updates when I publish something new.
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.
Dropout as Implicit Bagging
March 7, 2026
Chapter 7 clarified that dropout works so well because it approximates bagging over many thinned networks with shared parameters.