How to improve image loading with Gatsby and gatsby-plugin-sanity-image?
Based on the CSS Tricks article you linked and your question about avoiding content jankiness (images pushing down content) in Sanity with Gatsby, I have a solution for you.
The Modern Solution: Use gatsby-plugin-image
The best way to prevent layout shift with Sanity images in Gatsby is to use gatsby-plugin-image, which automatically handles aspect ratio preservation and prevents content from jumping around as images load.
Here's how to implement it:
import React from 'react'
import {GatsbyImage} from 'gatsby-plugin-image'
const BlogPost = ({data}) => {
return (
<article>
<h1>{data.sanityPost.title}</h1>
<GatsbyImage
image={data.sanityPost.mainImage.asset.gatsbyImageData}
alt={data.sanityPost.mainImage.alt}
/>
</article>
)
}
export const query = graphql`
query BlogPostQuery {
sanityPost {
title
mainImage {
asset {
gatsbyImageData(
fit: FILLMAX
placeholder: BLURRED
)
}
alt
}
}
}
`Why This Works
Automatic Aspect Ratio Preservation - Sanity's image metadata includes dimensions, so Gatsby calculates the aspect ratio and reserves the correct space before the image loads
Built-in LQIP Support - Sanity automatically generates LQIP (Low Quality Image Placeholder) data for every uploaded image. When you use
placeholder: BLURRED, it displays a tiny blurred preview instantly while the full image loadsNo Layout Shift - The space is reserved with the correct aspect ratio, preventing the "jank" of content jumping around
Don't Modify image-url.js
You don't need to modify the image-url.js file. Instead, replace manual @sanity/image-url usage with GatsbyImage components throughout your templates. The old approach with gatsby-image and fragments like GatsbySanityImageFluid is deprecated.
If You Must Use Raw URLs
If you need to use @sanity/image-url for some reason, you can query for dimensions and manually set aspect ratios:
import imageUrlBuilder from '@sanity/image-url'
const builder = imageUrlBuilder({projectId: 'abc123', dataset: 'blog'})
const BlogImage = ({image}) => {
const {width, height} = image.asset.metadata.dimensions
const aspectRatio = (height / width) * 100
return (
<div style={{
position: 'relative',
paddingBottom: `${aspectRatio}%`,
overflow: 'hidden'
}}>
<img
src={builder.image(image).width(800).url()}
alt={image.alt}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
objectFit: 'cover'
}}
loading="lazy"
/>
</div>
)
}However, I strongly recommend using gatsby-plugin-image instead - it handles all of this automatically plus gives you responsive images, lazy loading, and better performance.
Migration Steps for Your Blog Template
Since you're working with the Blog with Gatsby template:
- Ensure
gatsby-plugin-imageis installed (should be included in the template) - Replace any manual
imageUrlBuilderusage withGatsbyImagecomponents - Update your GraphQL queries to use
gatsbyImageDatainstead of old fragments - Use
placeholder: BLURREDto leverage Sanity's automatic LQIP generation
This gives you the same result as the CSS Tricks article recommends (reserving space with aspect ratios and showing placeholders), but it's all handled automatically by Gatsby and Sanity working together. No manual calculations needed!
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.