Sanity CMS Image Fallback for Draft Previews and Missing Media
Handle null Sanity image references in GROQ queries and React frontends by generating dimension-matched placeholder URLs for missing or unpublished media.
Sanity image fields hold a reference object, not a direct URL. When the field is empty, the reference is null. Passing a null reference to `@sanity/image-url` throws or silently returns an unusable object, and the component renders a broken image.
The fix is a short null check before calling the URL builder. When the reference is absent, substitute a generated fallback URL sized to match the destination slot. fallback.pics serves these from a public CDN with no authentication, so any frontend can use them without configuration.
How Sanity stores images
Sanity image references vs Contentful asset objects
Sanity stores images as separate documents in the dataset. An image field on a content document holds a `_ref` string pointing to the asset document, plus optional crop and hotspot data. When you query with GROQ, you get this reference object back — not the URL directly. You pass the reference to `@sanity/image-url` (the `urlFor()` builder) to construct a CDN URL.
The difference from Contentful matters: a null Contentful field gives you null. A null Sanity image field also gives you null, but code that checks `if (post.image)` will pass truthy for an empty object `{}` if the type is wrong. Use optional chaining: `post.image?._ref` is the actual indicator that an image document reference exists.
Portable Text documents can also embed inline images. Those follow the same pattern but appear inside an array of blocks, making systematic null checking even more important.
GROQ
Project image fields in your GROQ queries
Projecting the full image object in GROQ lets you check for null in one place at the query level, rather than scattering null checks through components. Use the `defined()` function in filters if you want to skip entries with missing images, or accept null and handle it on the frontend.
For performance, project only the fields you need from the asset document rather than dereferencing the full asset with `->`. The `_id`, `url`, and dimensions metadata are usually sufficient for building a fallback-aware image component.
// Fetch posts — include image dimensions for fallback matching
const query = groq`
*[_type == "post"] {
_id,
title,
slug,
"image": mainImage {
asset-> {
_id,
url,
metadata { dimensions }
},
crop,
hotspot
}
}
`; Fallback helper
Build a null-safe image URL helper
Wrap the `urlFor()` call in a helper that first checks whether the image reference exists. If it does not, return a fallback.pics URL with the slot dimensions as parameters. This keeps every component clean and consistent.
Use the projected metadata dimensions if available to match the aspect ratio of the original asset. When the image exists but you need a fallback during an error event, the stored width and height are the best source for dimension-correct placeholders.
// lib/sanity-image.ts
import imageUrlBuilder from '@sanity/image-url';
import { client } from './sanity-client';
const builder = imageUrlBuilder(client);
export function sanityImageUrl(
image: SanityImageRef | null | undefined,
width: number,
height: number,
): string {
if (image?.asset?._ref || image?.asset?.url) {
return builder.image(image).width(width).height(height).url();
}
return `https://fallback.pics/api/v1/${width}x${height}?text=Image+Missing`;
} Draft previews
Draft mode surfaces null images earlier
Sanity Studio previews use the Sanity API with `perspective: 'previewDrafts'`. Authors often write and preview posts before uploading the hero image. Without a fallback, the preview page shows a broken layout that creates distrust in the preview tool and leads editors to skip previews entirely.
Apply the same `sanityImageUrl` helper in preview data-fetching. Since preview routes typically run on-demand in Next.js, the fallback.pics URL is fast and does not require Sanity to have a real asset ready. Authors see a properly sized placeholder with a label instead of a broken icon.
Portable Text
Handle inline image blocks in Portable Text
Portable Text content can contain image blocks inline with paragraphs. When rendering with `@portabletext/react`, you provide a custom renderer for the `image` type. That renderer must handle null asset references.
Inline images in Portable Text rarely have predictable target dimensions. Use a sensible default like 800×500 for article body images, or read the image metadata dimensions if available.
import { PortableText } from '@portabletext/react';
import { sanityImageUrl } from '../lib/sanity-image';
const components = {
types: {
image: ({ value }) => {
const src = sanityImageUrl(value, 800, 500);
return (
<img
src={src}
alt={value.alt ?? ''}
width={800}
height={500}
loading="lazy"
/>
);
},
},
};
export function ArticleBody({ content }) {
return <PortableText value={content} components={components} />;
} Studio
Use fallback previews inside Sanity Studio
Sanity Studio document previews use a `preview` config on the schema type. The `media` field in the preview config points to an image field. When that field is empty, the Studio shows a generic icon. You can override the preview resolver to return a generated fallback URL so Studio editors see a properly sized card even for image-less entries.
This is a quality-of-life improvement for editorial teams: the document list looks consistent, and editors can clearly see which entries still need images uploaded rather than distinguishing between intentionally image-free content and entries with missing assets.
Resources
Further reading
The fallback.pics API supports text labels, custom colors, skeleton animations, and avatar routes. The CMS placeholder patterns guide covers Sanity alongside other headless CMS setups.
https://fallback.pics/docs/
https://fallback.pics/placeholder-image-api/
https://fallback.pics/blog/contentful-image-field-fallback/
https://fallback.pics/blog/placeholder-images-cms-previews-missing-media/ Key takeaways
What to standardize before shipping
- Check image?._asset?._ref, not just image, to determine if a Sanity image reference is set.
- Wrap urlFor() in a helper that returns a fallback.pics URL when the reference is absent.
- Match the fallback dimensions to the destination slot to prevent layout shift.
- Apply the same helper in draft preview routes so editors see a consistent preview layout.
- Handle the image block type in Portable Text renderers with the same null-safe helper.
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.