Unoptimized images in Vue
Images are usually the heaviest thing on a page, and when they ship without width/height they shift the layout as they load, when they ship at 2400px in a 400px slot they waste most of what they download, and when they load eagerly below the fold they steal bandwidth from what’s actually visible. AI scaffolds paste raw <img src> tags with none of the attributes that make images fast.
Symptoms
How VibeCheck catches it
In your widget · Problems
hero.png (2400×1200) missing width/height
To your coding agent · MCP
agent › get_detected_issues
→ { detector: "unoptimized-images", issue: "hero.png (2400×1200) missing width/height", threshold: "Natural size > 2× rendered size, or declared aspect ratio off by > 0.15, or missing dimensions/lazy/alt" }
The same string in your widget and in your agent’s context — no screenshot, no copy-paste.
Root causes
The fix for Vue
Set the same attributes in the template. On Nuxt, use <NuxtImg> from @nuxt/image for automatic resizing and modern formats.
<template>
<img src="/hero-800.webp" width="800" height="450" loading="lazy" alt="Product dashboard" />
<!-- Nuxt: <NuxtImg src="/hero.jpg" width="800" height="450" loading="lazy" /> -->
</template>Steps
- Add
widthandheightattributes matching the file’s real aspect ratio - Add
loading="lazy"to images below the fold (never to the LCP image) - Resize and re-encode to WebP/AVIF at the displayed size, with
srcsetfor density
FAQ
- Should I lazy-load every image?
- No — never lazy-load your LCP (largest, above-the-fold) image; that delays the most important paint. Lazy-load only images below the fold. In Next.js, mark the hero with
priority. - What does "oversized" mean exactly?
- VibeCheck flags an image whose natural (file) dimensions are more than twice its rendered (displayed) size — you’re downloading at least 4× the pixels you show. Resize to the displayed size, 2× for retina.
- Why does missing
width/heightcause layout shift? - Without dimensions the browser doesn’t know the image’s size until it downloads, so it reserves no space and everything below jumps when the image arrives. The attributes let it reserve the box up front.