Request for help building custom input component for Vimeo video preview
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:
- Component registration: Instead of
inputComponent, you now usecomponents.inputin your field definition - Props structure: The props your component receives are slightly different (though most are similar)
- Import paths: Many imports have moved (e.g.,
PatchEventis now just usingset()andunset()fromsanity)
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()andunset()from thesanitypackage for youronChangepatches - The
elementPropsshould be spread onto your input element for proper accessibility and focus management - Use components from
@sanity/ui(likeStack,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. 🙏
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.