TypeScript Props for Sanity Preview Component

6 replies
Last updated: Jan 28, 2026
There is not much info on the website on howTo define typescript props on a preview component. Tried creating mine but it’s not working I keep getting errors.


import { defineField, defineType } from 'sanity'
import getYouTubeId from 'get-youtube-id'
import LiteYouTubeEmbed from 'react-lite-youtube-embed'

import 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css'

type PreviewProps = {
  url: string
  title: string
  renderDefault: any
}

const Preview = ({
  url,
  title = 'YouTube Embed',
  renderDefault
}: PreviewProps) => {
  // const { url, title = 'YouTube Embed', renderDefault } = props

  if (!url) {
    return <div>Missing YouTube URL</div>
  }
  const id = getYouTubeId(url)

  return (
    <div>
      {renderDefault({ 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
  }
})
AI Update

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:

  1. Import PreviewProps from 'sanity' - This gives you the base type that includes essential props like renderDefault

  2. Create an extended type - Use a type intersection to add your selected fields:

    type YouTubePreviewProps = PreviewProps & {
      url?: string
      title?: string
    }
  3. 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} = castProps

This approach gives you full TypeScript support while working within Sanity's preview component architecture!

Show original thread
6 replies

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.

Was this answer helpful?