Astro Image Fallbacks for Content Sites and Docs
Handle missing and broken images in Astro using onerror handlers, the Image component, and fallback.pics placeholder URLs in content collections and layouts.
Astro handles images at two distinct layers: the built-in Image component for locally processed assets and plain img tags or frontmatter URLs for external or CMS-sourced media. Fallback handling differs between the two, and mixing them up is the most common source of broken images in Astro projects.
For content collections and docs sites, the most common failure is a missing or mistyped image path in frontmatter. A fallback URL pattern catches those errors before they reach production.
Context
Where Astro image fallbacks are actually needed
Astro's Image component processes local images at build time. If the source file is missing, the build fails with an error — which is often the right behavior in development. The failure mode you need to handle is runtime: remote images that return 404, CMS media fields that are empty, or blog post frontmatter with optional image fields.
The two main fallback surfaces in Astro are the img src attribute in .astro component templates and the image field in content collection schemas. Both need a different approach.
Basic fallback
onerror fallback on an img tag in Astro
For remote images rendered with a plain img tag, the onerror attribute handles the failure case client-side. This fires when the browser cannot load the image from the given src.
Use this pattern in card components, author avatars, and blog post thumbnails that display remote URLs from a CMS or external API.
Be careful not to create an infinite loop. If the fallback URL itself fails, onerror fires again. Setting this.onerror=null before changing the src prevents that.
<!-- In an Astro component -->
<img
src={post.image}
alt={post.imageAlt ?? post.title}
width="1200"
height="630"
onerror="this.onerror=null; this.src='https://fallback.pics/api/v1/1200x630/7C3AED/FFFFFF?text=Post+Image'"
/> Content collections
Handle optional image fields in content collection schemas
When you define an image field in a content collection schema with z.string().optional(), any post without an image field returns undefined. Resolve a fallback at the template level.
The cleanest pattern is a utility function that accepts the post entry and returns either the declared image or a generated fallback URL. This keeps template logic minimal and testable.
// src/utils/postImage.ts
import type { CollectionEntry } from 'astro:content';
export function postImage(entry: CollectionEntry<'blog'>) {
if (entry.data.image) return entry.data.image;
const text = encodeURIComponent(entry.data.title);
return `https://fallback.pics/api/v1/1200x630.jpg?text=${text}`;
}
// In an Astro page
---
import { getCollection } from 'astro:content';
import { postImage } from '../utils/postImage';
const posts = await getCollection('blog');
---
{posts.map(post => (
<img src={postImage(post)} alt={post.data.title} width="1200" height="630" />
))} Astro Image component
Fallback with Astro's built-in Image component
Astro's Image component does not support an onerror prop or a fallback src. If you need client-side fallback behavior on a processed image, render an unprocessed img tag alongside it, or use a client-side React or Svelte island.
For remote images you want to optimize through Astro's image pipeline, add the domain to the image configuration and use a null-check to skip optimization for URLs from less-reliable origins.
In practice, most Astro docs and blog sites benefit from using Image for locally stored assets and plain img with an onerror for all externally sourced media.
// astro.config.mjs
export default defineConfig({
image: {
domains: ['images.example.com', 'cdn.example.com'],
},
});
<!-- Reliable local asset — use Image -->
<Image src={localImage} alt="Post thumbnail" width={1200} height={630} />
<!-- Remote CMS image — use img + onerror -->
<img
src={cmsImageUrl}
alt="Post thumbnail"
width="1200"
height="630"
onerror="this.onerror=null; this.src='https://fallback.pics/api/v1/1200x630?text=Missing+Image'"
/> OG image
Set og:image fallbacks in Astro layouts
Blog posts built from content collections should always have an og:image in the page head, even if no featured image was set. Compute the fallback URL in the layout component before it is used in the meta tag.
The thumbnail route produces an image that renders well in social link previews because it places the post title in a readable text zone. Request .jpg from the thumbnail route for maximum platform compatibility.
---
// src/layouts/BlogLayout.astro
const { post } = Astro.props;
const ogImage = post.data.image
?? `https://fallback.pics/api/v1/thumbnail/1200x630.jpg?text=${encodeURIComponent(post.data.title)}&label=Blog`;
---
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" /> Performance
Always declare width, height, and loading attributes
A missing width and height on an img tag causes layout shift when the image loads. Always include explicit dimensions, especially for fallback images that may not match the dimensions of the original.
Set loading='lazy' on below-the-fold images and loading='eager' on hero images. For placeholder fallback images in a grid, lazy loading prevents unnecessary network requests for cards that never enter the viewport.
<img
src={postImage(post)}
alt={post.data.title}
width="800"
height="450"
loading="lazy"
decoding="async"
onerror="this.onerror=null; this.src='https://fallback.pics/api/v1/800x450?text=Image+Unavailable'"
/> Key takeaways
What to standardize before shipping
- Set this.onerror=null before updating this.src to prevent infinite fallback loops on broken fallback URLs.
- Resolve optional content collection image fields in a utility function, not inline in template markup.
- Astro's Image component does not accept onerror; use a plain img tag for externally sourced media.
- Always set width and height on img tags — including fallback images — to prevent layout shift.
- Compute og:image fallback URLs in the layout component so every page has a valid social preview image.
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.