Gatsby Image Optimization: Choosing Between gatsby-plugin-image and Manual Approaches
Choose gatsby-plugin-image for automatic responsive images, lazy-loading, and WebP conversion. Avoid mixing with legacy gatsby-image to prevent import errors.
27 Jul 2026, 04:56 UTC

The Decision: Modern Image Handling in Gatsby
When building a Gatsby site, image optimization directly impacts performance scores and user experience. The choice between gatsby-plugin-image and manual approaches isn't just about convenience—it affects build times, bundle sizes, and maintenance overhead.
Constraints and Context
This decision applies to Gatsby v4+ projects where performance metrics matter. Legacy Gatsby projects using gatsby-image must migrate, and dynamic image sources require proper source configuration.
Comparison of Image Handling Methods
| Feature | gatsby-plugin-image | Legacy gatsby-image | Manual img tags |
|---|---|---|---|
| Responsive srcset | Automatic | Manual config | Manual |
| Lazy loading | Built-in | Plugin required | Native loading attr |
| WebP/AVIF conversion | Automatic | No | CDN dependent |
| Build-time processing | Yes | Yes | No |
| GraphQL integration | Native | Limited | None |
Trade-offs Analysis
gatsby-plugin-image excels at developer experience but requires understanding of the new API. The automatic optimizations typically reduce image payload substantially compared to manual approaches, but you give up fine-grained control over the exact formats served.
Legacy gatsby-image creates import conflicts when mixed with the new plugin. Migration is necessary for projects expecting modern performance scores, especially under Lighthouse audits.
Manual img tags seem flexible but increase maintenance burden. Each responsive breakpoint must be hand-coded, and you'll likely miss WebP conversion unless your CDN handles it automatically.
Implementation Example
Here's a minimal setup using gatsby-plugin-image. Run all commands from your project root; no elevated permissions are needed beyond normal Node.js install rights.
// gatsby-config.js
module.exports = {
plugins: [
"gatsby-plugin-image",
"gatsby-plugin-sharp",
"gatsby-transformer-sharp",
{
resolve: "gatsby-source-filesystem",
options: {
name: "images",
path: `${__dirname}/src/images`,
},
},
],
}
// src/pages/index.js
import React from "react"
import { graphql } from "gatsby"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
const IndexPage = ({ data }) => {
const image = getImage(data.file)
return <GatsbyImage image={image} alt="Example product" />
}
export const query = graphql`
query {
file(relativePath: { eq: "product.jpg" }) {
childImageSharp {
gatsbyImageData(width: 800, placeholder: BLURRED)
}
}
}
`
export default IndexPage
Replace product.jpg with a file that actually exists in src/images. The getImage helper safely extracts the image data object the component expects.
Verification Steps
- Build check: Run
gatsby buildfrom the project root and confirm image variants (including WebP where configured) appear underpublic/static - HTML inspection: Open the built HTML and confirm
imgtags includesrcsetandloading="lazy"attributes - DevTools inspection: In Chrome DevTools Network tab, reload the page and confirm offscreen images load only as you scroll toward them
- Lighthouse audit: Run a Lighthouse Performance audit and check the image-related opportunities report improvements
Important Limitations
gatsby-plugin-image requires Gatsby v4 or later. Projects on older versions must either upgrade or stay on legacy gatsby-image. Dynamic remote images need additional source plugin configuration—plain remote URLs are not optimized automatically at build time.
Migration note: Mixing gatsby-plugin-image components with legacy gatsby-image in the same project causes import and runtime errors. Audit all image components before switching plugins.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.