Request for help building custom input component for Vimeo video preview

3 replies
Last updated: Jun 12, 2023
Hi all, I'm currently looking to build a custom input component that previews a Vimeo video based on a text input. The text input would expect an ID or URL pointing to the Vimeo video. This example (https://www.sanity.io/schemas/vimeo-youtube-video-id-and-thumbnail-custom-component-4a86c2f5 ) is essentially what I'm looking for, although this has been written for the V2 Studio. I'm aware that there is a well documented V2-V3 migration guide, yet I thought it would be worth asking if there are existing versions of the aforementioned example written for V3. I'm still a react novice and unfamiliar with the process of writing custom input components and was hoping people could give me pointers. Any help is greatly appreciated and thank you in advance! 🙏
AI Update

Great question! While that v2 schema example is a bit dated, the good news is that migrating it to v3 is pretty straightforward once you understand the key differences. Let me walk you through what's changed and point you to some helpful resources.

Key Differences from V2 to V3

The main changes for custom input components in Studio v3 are:

  1. Component registration: Instead of inputComponent, you now use components.input in your field definition
  2. Props structure: The props your component receives are slightly different (though most are similar)
  3. Import paths: Many imports have moved (e.g., PatchEvent is now just using set() and unset() from sanity)

Building a Vimeo Preview Component for V3

Here's the basic structure you'll need. First, your schema definition:

{
  name: 'vimeoVideo',
  type: 'string',
  title: 'Vimeo Video',
  components: {
    input: VimeoPreviewInput  // This is the v3 way
  }
}

And here's a basic v3 custom input component to get you started:

import { set, unset } from 'sanity'
import { Stack, TextInput, Card } from '@sanity/ui'
import { useCallback, useState, useEffect } from 'react'

export function VimeoPreviewInput(props) {
  const { value, onChange, elementProps } = props
  const [thumbnailUrl, setThumbnailUrl] = useState(null)
  
  // Extract Vimeo ID from URL or use raw ID
  const getVimeoId = (input) => {
    if (!input) return null
    const match = input.match(/vimeo\.com\/(\d+)/)
    return match ? match[1] : input
  }
  
  const vimeoId = getVimeoId(value)
  
  // Fetch Vimeo thumbnail
  useEffect(() => {
    if (vimeoId) {
      fetch(`https://vimeo.com/api/v2/video/${vimeoId}.json`)
        .then(res => res.json())
        .then(data => setThumbnailUrl(data[0]?.thumbnail_large))
        .catch(() => setThumbnailUrl(null))
    }
  }, [vimeoId])
  
  const handleChange = useCallback((event) => {
    const inputValue = event.currentTarget.value
    onChange(inputValue ? set(inputValue) : unset())
  }, [onChange])
  
  return (
    <Stack space={3}>
      <TextInput
        {...elementProps}
        value={value || ''}
        onChange={handleChange}
        placeholder="Enter Vimeo ID or URL"
      />
      {thumbnailUrl && (
        <Card padding={2} border>
          <img 
            src={thumbnailUrl} 
            alt="Vimeo preview" 
            style={{ width: '100%', height: 'auto' }}
          />
        </Card>
      )}
    </Stack>
  )
}

Essential Resources

I'd highly recommend checking out this guide on creating your first input component for Studio v3 - it walks through all the fundamentals with practical examples.

The official form components documentation is also super helpful for understanding all the props your component receives and best practices.

Key Things to Remember

  • Always use set() and unset() from the sanity package for your onChange patches
  • The elementProps should be spread onto your input element for proper accessibility and focus management
  • Use components from @sanity/ui (like Stack, Card, TextInput) to maintain visual consistency with Studio
  • For production use, consider adding error handling for failed API requests and loading states

Since you mentioned you're a React novice, the pattern is basically: your component receives value and onChange as props (just like a controlled React component), and you call onChange(set(newValue)) whenever the user makes a change. The Vimeo API part is just a standard useEffect + fetch pattern.

Hope this helps get you started! Feel free to ask if you run into any specific issues while building it out. 🙏

No, unfortunately there isn’t a guide for this. The good news is that the majority of the component can be rendered using the
renderDefault
method that’s passed into your props.
user M
thank you for getting back, I really appreciate you taking the time to respond to this. I'm not entirely sure how to use the renderDefault functionality, specifically in relation to this example. Is there a specific documentation that I can learn from?
Sure! There’s more information about that method here .

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?