Trouble defining TypeScript props on a preview component in Sanity
The issue with your code is that you need to properly type your preview component props. While PreviewProps can be imported from 'sanity', it doesn't automatically know about the custom fields you're selecting in your preview.select configuration. You need to extend it with a type cast.
Here's your corrected code:
import { defineField, defineType } from 'sanity'
import type { PreviewProps } from 'sanity'
import getYouTubeId from 'get-youtube-id'
import LiteYouTubeEmbed from 'react-lite-youtube-embed'
import 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css'
// Extend PreviewProps with your selected fields
type YouTubePreviewProps = PreviewProps & {
url?: string
title?: string
}
const Preview = (props: PreviewProps) => {
// Cast to access your custom fields
const { url, title = 'YouTube Embed' } = props as YouTubePreviewProps
if (!url) {
return <div>Missing YouTube URL</div>
}
const id = getYouTubeId(url)
return (
<div>
{props.renderDefault({ ...props, title })}
<LiteYouTubeEmbed title={title} id={id as string} />
</div>
)
}
export default defineType({
name: 'youtube',
type: 'object',
title: 'YouTube Embed',
fields: [
defineField({
name: 'title',
type: 'string',
title: 'YouTube video title'
}),
defineField({
name: 'url',
type: 'url',
title: 'YouTube video URL'
})
],
preview: {
select: {
title: 'title',
url: 'url'
}
},
components: {
preview: Preview
}
})Key changes and why they matter:
Import
PreviewPropsfrom'sanity'- This gives you the base type that includes essential props likerenderDefaultCreate an extended type - Use a type intersection to add your selected fields:
type YouTubePreviewProps = PreviewProps & { url?: string title?: string }Cast the props - Since TypeScript can't automatically infer what fields come from
preview.select, you need to cast:props as YouTubePreviewProps
Why preview components work differently: According to the official guide on creating richer array item previews, preview components don't receive the field's actual value or path like other form components. Instead, they only get the values you explicitly define in preview.select. This is why you need to manually type-cast to access those fields.
The documentation shows this exact pattern:
type CastPreviewProps = PreviewProps & {
discount?: number
validUntil?: string
}
const castProps = props as CastPreviewProps
const {discount, validUntil} = castPropsThis approach gives you full TypeScript support while working within Sanity's preview component architecture!
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.