Gatsby Image Fallback When gatsby-plugin-image Fails
Handle broken and missing images in Gatsby with GatsbyImage null checks, onError handlers, and fallback.pics URLs for empty CMS media fields and external product photos.
gatsby-plugin-image processes images at build time and creates optimized responsive variants. When a CMS returns null for an image field, or when the source file is missing, the build either fails or renders an empty slot depending on how the query is structured.
Fallback patterns for Gatsby split into two cases: build-time null handling for GraphQL image nodes, and runtime onError handling for external images loaded outside the Gatsby image pipeline.
Two failure modes
Build-time null vs runtime 404 in Gatsby images
gatsby-plugin-image optimizes images during gatsby build by processing source nodes from GraphQL. If a CMS image field returns null and your query treats it as required, the build fails. If it is nullable, the image slot renders nothing.
The second failure mode is runtime: an img tag whose src is an external URL that returns 404 after the page loads. This is common for product catalogs, user content, and any image URL stored as a plain string in a CMS rather than as a processed media node.
Most Gatsby projects need both approaches: null-checks on processed image fields and onError handlers on plain img tags.
Null handling
Handle null CMS image fields in Gatsby page templates
In a Gatsby page template, use optional chaining when accessing the image node. The GatsbyImage component requires a valid gatsbyImageData prop — passing null or undefined causes a runtime error.
When the CMS field is null, fall back to a plain img tag with a generated fallback URL. This keeps the page rendering without crashing and still shows something useful.
// templates/post.tsx
export default function PostTemplate({ data }) {
const { post } = data;
const image = post.frontmatter.coverImage;
return (
<article>
{image ? (
<GatsbyImage
image={image.childImageSharp.gatsbyImageData}
alt={post.frontmatter.title}
/>
) : (
<img
src={`https://fallback.pics/api/v1/1200x630?text=${encodeURIComponent(post.frontmatter.title)}`}
alt={post.frontmatter.title}
width={1200}
height={630}
/>
)}
</article>
);
} GraphQL query
Mark image fields as optional in GraphQL queries
In Gatsby's GraphQL layer, image fields sourced from local files or a CMS can be null if the file is missing. Mark the entire image block as optional so that a missing image does not fail the entire page build.
For Contentful or Sanity-sourced images, the childImageSharp node is only available when Gatsby has processed the file. A remote URL stored as a plain string will not have a childImageSharp node at all.
query PostQuery($id: String!) {
post: mdx(id: { eq: $id }) {
frontmatter {
title
coverImage {
childImageSharp {
gatsbyImageData(width: 1200, height: 630, placeholder: BLURRED)
}
}
}
}
} External images
onError fallback for externally sourced images
Any image loaded via a plain img tag — product photos from an API, user-uploaded content, or CMS URLs not processed by gatsby-plugin-image — can fail at runtime. Add an onError handler.
In React (which Gatsby uses), use the onError prop. Update state to swap to the fallback URL and prevent retries by checking the current src value before setting the fallback.
// components/ProductCard.tsx
export function ProductCard({ product }) {
const [src, setSrc] = useState(product.imageUrl);
const fallback = `https://fallback.pics/api/v1/400x400?text=${encodeURIComponent(product.name)}`;
return (
<img
src={src}
alt={product.name}
width={400}
height={400}
onError={() => { if (src !== fallback) setSrc(fallback); }}
/>
);
} OG image
Set og:image fallbacks in Gatsby Head API
Gatsby's Head API lets you export a Head function from any page or template. Use it to compute a fallback og:image when the page has no featured image.
The thumbnail route is a good fit here because it produces a blog-card style image with the post title embedded — better for social previews than a plain color block.
// templates/post.tsx
export function Head({ data }) {
const { post } = data;
const ogImage = post.frontmatter.coverImage
? post.frontmatter.coverImage.publicURL
: `https://fallback.pics/api/v1/thumbnail/1200x630.jpg?text=${encodeURIComponent(post.frontmatter.title)}`;
return (
<>
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
</>
);
} Tradeoffs
Static fallback image vs generated URL
A static fallback image — a single file committed to the repo — is simpler to manage and guarantees availability. It breaks down when posts benefit from different visual treatment by category or topic.
A generated URL from fallback.pics is more dynamic: it embeds the post title, supports category labels and color themes, and requires no additional assets. The tradeoff is a dependency on the external service, though for a last-resort fallback that dependency is acceptable.
Static file fallback
Zero external dependency. Same image for all posts. Easy to serve from CDN.
Generated URL fallback
Per-post title and category. No asset management. Depends on external API.
Build-time check
Fail the build on missing images to catch content errors before deploy.
Key takeaways
What to standardize before shipping
- Render GatsbyImage when the image node exists; fall back to a plain img with a generated URL when the CMS field is null.
- Mark image fields as optional in GraphQL queries to prevent build failures when media is missing.
- Add onError handlers to plain img tags for externally sourced images that gatsby-plugin-image does not process.
- Use the Gatsby Head API to set og:image fallbacks so social crawlers receive a valid URL for every page.
- A generated URL with title and category parameters provides per-post visual variety without managing static fallback files.
Production fallback layer
Use fallback.pics anywhere an image URL is accepted.
Start with one deterministic URL and standardize fallback behavior across your design system.