Fix Incorrect Image Aspect Ratio for Better Best Practices Score
When an image's display dimensions don't match its natural aspect ratio, it gets stretched or squished. Faces look wrong. Products look unprofessional. Users notice.
What's Happening
You have an image with natural dimensions of 1200x800 (aspect ratio 1.5:1), but you're displaying it at 400x400 (aspect ratio 1:1). The browser has three choices: stretch it, squish it, or crop/contain it. Without explicit object-fit instructions, it stretches.
Lighthouse compares the displayed aspect ratio to the natural aspect ratio. If they differ by more than a couple pixels (accounting for rounding), the audit fails. It only checks images using object-fit: fill (the default) because other values intentionally override aspect ratio behavior.
Diagnose
Chrome DevTools
- Right-click the distorted image, select Inspect
- Check the element's rendered dimensions vs natural dimensions:
// Select the image element first
const img = $0
console.log({
natural: `${img.naturalWidth}x${img.naturalHeight}`,
displayed: `${img.clientWidth}x${img.clientHeight}`,
naturalRatio: (img.naturalWidth / img.naturalHeight).toFixed(2),
displayedRatio: (img.clientWidth / img.clientHeight).toFixed(2)
})
If the ratios don't match, that's your problem.
Find All Distorted Images
document.querySelectorAll('img').forEach((img) => {
if (!img.naturalWidth || !img.naturalHeight)
return
if (img.clientWidth < 5 || img.clientHeight < 5)
return
const naturalRatio = img.naturalWidth / img.naturalHeight
const displayedRatio = img.clientWidth / img.clientHeight
const diff = Math.abs(naturalRatio - displayedRatio)
if (diff > 0.05) {
console.warn('Aspect ratio mismatch:', img.src, {
natural: `${img.naturalWidth}x${img.naturalHeight} (${naturalRatio.toFixed(2)})`,
displayed: `${img.clientWidth}x${img.clientHeight} (${displayedRatio.toFixed(2)})`
})
}
})
Fix
1. Match Container to Image Ratio
The cleanest fix: make the display dimensions match the natural aspect ratio.
<!-- Image is 1200x800 (1.5:1 ratio) -->
<!-- Bad: forcing into a square -->
<img src="/photo.jpg" style="width: 400px; height: 400px;">
<!-- Good: maintaining ratio -->
<img src="/photo.jpg" style="width: 400px; height: 267px;">
<!-- Better: let height auto-calculate -->
<img src="/photo.jpg" width="1200" height="800" style="width: 400px; height: auto;">
With width and height attributes, browsers calculate the aspect ratio automatically:
img {
max-width: 100%;
height: auto;
}
2. Use object-fit for Fixed Containers
When you need a specific container size regardless of image ratio, use object-fit:
.thumbnail {
width: 200px;
height: 200px;
object-fit: cover; /* Crop to fill, maintain ratio */
}
.product-image {
width: 100%;
height: 300px;
object-fit: contain; /* Fit inside, maintain ratio, may letterbox */
}
object-fit values:
cover- Fill container, crop excess (most common for thumbnails)contain- Fit inside container, letterbox if neededfill- Stretch to fill (default, causes distortion)none- Natural size, crop if largerscale-down- Likecontainbut never upscales
<div class="avatar">
<img src="/user.jpg" alt="User" style="width: 100%; height: 100%; object-fit: cover;">
</div>
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
overflow: hidden;
}
3. Use aspect-ratio for Responsive Containers
Let CSS maintain the ratio while the container scales:
.video-thumbnail {
width: 100%;
aspect-ratio: 16 / 9;
}
.video-thumbnail img {
width: 100%;
height: 100%;
object-fit: cover;
}
The container maintains 16:9, and object-fit: cover handles any image that doesn't match exactly.
4. Crop Images at Source
If every image in a gallery should be square, crop them to square before uploading rather than relying on CSS:
// Server-side with Sharp
import sharp from 'sharp'
sharp('input.jpg')
.resize(400, 400, {
fit: 'cover',
position: 'attention' // Smart crop
})
.toFile('output-square.jpg')
This gives you control over what gets cropped rather than leaving it to CSS.
Framework Examples
NuxtImg with fit prop:<template>
<NuxtImg
src="/photo.jpg"
width="400"
height="400"
fit="cover"
alt="Photo"
/>
</template>
<div class="aspect-square">
<NuxtImg src="/photo.jpg" class="w-full h-full object-cover" />
</div>
next/image with fill and objectFit:<div className="relative w-48 h-48">
<Image
src="/photo.jpg"
fill
style={{ objectFit: 'cover' }}
alt="Photo"
/>
</div>
<Image
src="/photo.jpg"
width={1200}
height={800}
style={{ width: '100%', height: 'auto' }}
alt="Photo"
/>
Verify the Fix
- Visual check - Images should look natural, not stretched or squished
- Re-run Lighthouse - "Displays images with correct aspect ratio" should pass
- Console check:
document.querySelectorAll('img').forEach((img) => {
const style = getComputedStyle(img)
if (style.objectFit !== 'fill')
return // Using cover/contain is fine
const naturalRatio = img.naturalWidth / img.naturalHeight
const displayedRatio = img.clientWidth / img.clientHeight
if (Math.abs(naturalRatio - displayedRatio) > 0.05) {
console.warn('Still distorted:', img.src)
}
})
Common Mistakes
- Setting only width -
width: 100%withoutheight: autocan cause stretching if there's an inherited height. - Forgetting object-fit on avatars - Circle avatars with square images need
object-fit: coveror they'll oval. - Wrong width/height attributes - If your HTML says 400x300 but the image is 1200x800, browsers use the attributes for ratio calculation. Make them match.
- Background images - This audit only checks
<img>elements. Distorted background images needbackground-size: coverorcontain.
Test Your Entire Site
Your homepage might look perfect while product pages stretch images into a grid. Unlighthouse checks every page and surfaces all distorted images across your entire site.
Related
Geolocation
Stop requesting geolocation permission on page load. Users distrust sites that ask for location without context - tie requests to user actions instead.
Low Resolution Images
Serve images with appropriate resolution for the display size and device pixel ratio. Blurry images hurt perceived quality.