Gatsby Image Optimization with gatsby-plugin-image: Practical Implementation and Trade-offs
Replace manual childImageSharp work with gatsby-plugin-image. This post shows a complete, working page component and MDX example, explains the build-time pipeline, and lists concrete verification steps plus the real trade-offs (build time, placeholder size, Sharp deps).
09 Mar 2026, 09:33 UTC

The Problem: Manual Image Handling Doesn't Scale
Gatsby sites that rely on the legacy childImageSharp API often ship oversized images in a single format. Developers manually configure breakpoints, formats, and placeholders—work that grows tedious and error-prone as the image count rises. The result is slower page loads, especially on mobile, and a maintenance burden that discourages consistent optimization.
Thesis: gatsby-plugin-image Automates Responsive Delivery
The gatsby-plugin-image package replaces the imperative childImageSharp approach with a declarative <GatsbyImage> component. At build time, Sharp generates multiple formats (AVIF, WebP, JPEG/PNG) and widths, then exposes a gatsbyImageData GraphQL fragment containing srcset, sizes, and placeholder data. The component renders a low-resolution placeholder immediately and lazy-loads the optimal variant via the browser's native srcset selection.
How the Pipeline Works
- Source ingestion:
gatsby-source-filesystemadds image files to Gatsby's data layer. - Transform:
gatsby-transformer-sharpcreatesImageSharpnodes for each file. - Processing:
gatsby-plugin-sharp(Sharp ≥ 0.32 for AVIF on Node 18+) encodes derivatives at the dimensions and formats you request. - Query: Your page or component requests
gatsbyImageDatawith options likewidth,placeholder, andformats. - Render:
<GatsbyImage image={getImage(data.file)} alt="…" />outputs a semantic<img>withloading="lazy",srcset, and a base64 or traced-SVG placeholder.
Worked Example: A Hero Image in a Page Component
Assume the file lives at src/images/hero.jpg and the filesystem source is configured for src/images.
// src/pages/index.js
import * as React from "react"
import { graphql } from "gatsby"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
export const query = graphql`
query HeroQuery {
hero: file(relativePath: { eq: "hero.jpg" }) {
childImageSharp {
gatsbyImageData(
width: 1200
placeholder: BLURRED
formats: [AUTO, AVIF, WEBP]
)
}
}
}
`
export default function Home({ data }) {
const image = getImage(data.hero)
return (
)
}
The getImage helper extracts the fragment; <GatsbyImage> handles the rest. The AUTO format token lets the plugin choose the best available format per browser.
Using the Same Pattern in MDX
MDX pages can run a page query and pass the result to <GatsbyImage> inside the markup.
// src/pages/post/my-post.mdx
import { GatsbyImage, getImage } from "gatsby-plugin-image"
export const query = graphql`
query PostHero {
hero: file(relativePath: { eq: "hero.jpg" }) {
childImageSharp {
gatsbyImageData(
width: 1200
placeholder: TRACED_SVG
formats: [AUTO, AVIF, WEBP]
)
}
}
}
`
export default function Post({ data }) {
const heroImage = getImage(data.hero)
return (
{heroImage && (
)}
)
}
The component wrapper receives data from Gatsby's page query, so getImage has the fragment it needs.
Verification Steps You Can Run Today
- Install and configure (run in project root):
Add the plugins tonpm install gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp gatsby-source-filesystemgatsby-config.jsin the order shown in the research brief. - Start the dev server:
gatsby develop. Open the page and inspect the rendered<img>—look forsrcsetentries ending in.avif,.webp,.jpgand asizesattribute. - Check network requests: In Chrome DevTools → Network → Img, confirm the browser downloads the format it supports (AVIF on Chrome 115+, WebP on older Chrome/Firefox, JPEG on Safari).
- Run a Lighthouse audit against a production build URL:
Verify "Properly size images" and "Defer offscreen images" pass.npx lighthouse https://your-site.com --output=json --output-path=lh.json - Inspect the production output: After
gatsby build, checkpublic/static/for.avif,.webp, and.jpgfiles matching your source names. The build log should list "Processing image" lines.
Trade-offs and Limitations
- Build time grows with image count. Each unique image/width/format combination invokes Sharp. Incremental builds (enabled by default in Gatsby Cloud and via
GATSBY_EXPERIMENTAL_FAST_DEVlocally) reprocess only changed images, but a full CI build still scales linearly. For very large sites, consider a separate image CDN or build-time caching strategies. - Placeholder payload.
BLURREDembeds a base64 data URI (~1–2 KB per image) in the JavaScript bundle.TRACED_SVGis smaller but more CPU-intensive to generate.DOMINANT_COLORis the lightest. Choose based on your bundle-size budget. - Sharp native dependencies. On Alpine or minimal Linux images, install
libjpeg-dev libpng-dev libwebp-devbeforenpm installto avoid compile failures. - AVIF browser support. Safari 16+ supports AVIF; earlier versions fall back to WebP/JPEG automatically. Test on your target browser matrix.
- Cache invalidation. The plugin hashes the source file content. Changing an image file triggers new derivatives on the next build. Incremental builds handle this without a full clean rebuild, but the new hash means browsers will re-fetch the updated asset.
- Missing source files break GraphQL. If a file referenced in a query is absent, the build fails. Keep assets under version control or guard queries with optional chaining where appropriate.
Actionable Takeaways
- Migrate new images to
gatsby-plugin-imagenow; it is the supported path forward. - Start with
formats: [AUTO, WEBP]and addAVIFonce you verify Sharp ≥ 0.32 and Node 18+ in your build environment. - Use
TRACED_SVGorDOMINANT_COLORplaceholders for image-heavy pages to keep the JS bundle lean. - Enable incremental builds in your CI/CD pipeline to avoid reprocessing unchanged images on every deploy.
- Run the verification checklist above before each major release to catch regressions early.
With these steps, your Gatsby site serves appropriately sized, modern-format images to every browser while keeping build times and bundle weight under control.