
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.
One manifest instead of scattered assumptions
The main change was replacing repeated conventions with one manifest-driven path. There is now one canonical workflow: yarn image:variants. Rendering logic is less duplicated because a shared ResponsiveImage component replaced repeated <picture> patterns. Static assets also became data-driven, so background and portrait srcSet values now come from manifest-backed metadata rather than JSX literals.
A simpler authoring path
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.
Stricter failure modes
Once maintainability improved, performance work became easier to verify. 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.
What I actually fixed
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
My AI Coding Workflow: Planning, Review, and Verification
February 3, 2026
How my AI coding workflow moved from quick snippets to clearer briefs, agent review checkpoints, and build/lint/test verification.
The 'Boring' Architecture Behind This Blog
January 31, 2026
How I added a fully static, markdown-based blog to my Next.js portfolio.
Camping Indoors in San Francisco
April 27, 2026
My first week in San Francisco has mostly been logistics, empty rooms, cats, and the slow work of making enough space to focus.