Handling empty content in Sanity previews with fallbacks
This is a common challenge! Here are several approaches you can use to handle empty content gracefully in your previews:
1. Custom Preview with Fallbacks in Schema
The most straightforward approach is using the preview configuration in your schema with a prepare function that handles empty states:
export default {
name: 'myModule',
type: 'object',
fields: [
{name: 'title', type: 'string'},
{name: 'image', type: 'image'},
{name: 'content', type: 'text'}
],
preview: {
select: {
title: 'title',
image: 'image',
content: 'content'
},
prepare({title, image, content}) {
return {
title: title || '⚠️ No title added',
subtitle: content || 'Click to add content',
media: image || () => '📝' // or import an icon from @sanity/icons
}
}
}
}You can even provide more helpful feedback:
prepare({title, image, content}) {
const missing = []
if (!title) missing.push('title')
if (!image) missing.push('image')
if (!content) missing.push('content')
return {
title: title || 'Incomplete module',
subtitle: missing.length ? `Missing: ${missing.join(', ')}` : 'Ready',
media: image
}
}2. Add Validation Rules
Prevent empty content from being saved in the first place using validation rules:
{
name: 'image',
type: 'image',
validation: Rule => Rule.required().error('Please add an image')
},
{
name: 'content',
type: 'text',
validation: Rule => Rule.required().warning('Content is recommended')
}3. Defensive Coding in Your Frontend Preview
Always check for empty values in your actual preview/frontend code:
function ModuleComponent({ module }) {
if (!module?.image || !module?.content) {
return (
<div className="preview-placeholder">
<p>⚠️ This module needs content</p>
<ul>
{!module?.image && <li>Add an image</li>}
{!module?.content && <li>Add content</li>}
</ul>
</div>
)
}
return (
// your normal component
)
}Or use fallback values with optional chaining:
const imageUrl = module?.image?.asset?.url ?? '/placeholder-image.jpg'
const title = module?.title ?? 'Untitled'
const content = module?.content ?? 'No content yet'4. Custom Input Components
For more complex scenarios, you can create a custom input component that shows warnings directly in the editing interface:
const CustomModuleInput = (props) => {
const {title, image, content} = props.value || {}
const hasEmptyFields = !title || !image || !content
return (
<Stack space={3}>
{hasEmptyFields && (
<Card tone="caution" padding={3}>
<Text>⚠️ Please complete all fields for this module</Text>
</Card>
)}
{props.renderDefault(props)}
</Stack>
)
}
// In your schema:
{
name: 'myModule',
type: 'object',
components: {
input: CustomModuleInput
}
}The combination of preview configuration with fallbacks (for Studio UI) and defensive coding in your frontend (for actual preview rendering) usually works best. This way editors get clear feedback about what's missing, and your preview never breaks even if incomplete content slips through.
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.