Sanity logosanity.ioAll Systems Operational© Sanity 2026
Change Site Theme
Sanity logo

Documentation

    • Overview
    • Platform introduction
    • Next.js quickstart
    • Nuxt.js quickstart
    • Astro quickstart
    • React Router quickstart
    • Studio quickstart
    • Build with AI
    • Content Lake
    • Functions
    • APIs and SDKs
    • Agent Actions
    • Visual Editing
    • Blueprints
    • Platform management
    • Dashboard
    • Studio
    • Canvas
    • Media Library
    • App SDK
    • Content Agent
    • HTTP API
    • CLI
    • Libraries
    • Specifications
    • Changelog
    • User guides
    • Developer guides
    • Courses and certifications
    • Join the community
    • Templates
Help articles
Overview

  • Array items resolve to same JSON type
  • Studio Performance Issues Caused by legacy HTTP protocols
  • Error: Value of type "object" is not allowed in this array field
  • AVIF
  • Experimental feature: Spaces
  • Client API CDN configuration
  • Total attribute count exceeds limit
  • Desk is now Structure
  • Invalid configuration for cross dataset reference
  • Missing or duplicate context error
  • Sanity Studio v2 is deprecated
  • React Compiler and Sanity
  • Specify API version for studio client
  • Why give schema types a title?
  • Array type has a invalid value for property "of"
  • React 19 and Sanity
  • Schema: Lift anonymous object types
  • Reference type has a invalid value for property "to"
  • Incorrect location for reference options
  • Invalid part syntax
  • Asset metadata field
  • Warning: userStore.currentUser is deprecated
  • CLI errors
  • Renamed plugin sanity-plugin-vision
  • Part name format
  • Array member type name is the same as a global type
  • Changes in block schema customization properties
  • How to migrate from date to richDate
  • Invalid shape of predefined choices
  • JS Client: Promise Polyfill
  • Introducing the document type
  • Unable to get a ref to an input component
  • Authenticating the CLI when running remotely
  • Outdated modules
  • Upgrade studio packages
  • Block Content rendering: Image materializing
  • Structure: Document schema type required
  • Parts: Declare vs implement
  • Incorrect options declaration in reference
  • Block type cannot be used outside of array
  • Structure: Node ID required
  • Structure: List items must be an array
  • Installing Node.js
  • Structure: Action or intent required
  • Object type has a invalid value for fields
  • `studioHost` and `externalStudioHost` properties deprecated
  • Schema type is ES Module but imported through require
  • Structure: Invalid list item
  • Structure: Query provided where filter is expected
  • Structure: List item IDs must be unique
  • Given type name is a reserved type
  • Structure: Schema type not found
  • API versioning
  • Migrating the legacy webhook behavior to GROQ-powered Webhooks
  • Schema type is invalid
  • Input component is missing a required prop
  • Structure: Title is required
  • Structure: Filter is required
  • Import: Asset file does not exist
  • Input component is missing a required method
  • Implementing non-overridable part
  • Structure: Item returned no child
  • How to migrate your block text schema for the new definition of inline objects
  • Structure: Schema type is required
  • How to migrate from blocks spans to block children
  • Array type cannot contain array member
  • Using tokens in the browser
  • GraphQL
  • Array member type name conflicts with built-in type
  • Source vs. compiled paths
  • Import: Asset has different target than source
  • Using global studio client without specifying API version
  • Structure: Action and intent are mutually exclusive
  • Upgrade React
  • Plugin is missing a sanity.json file
  • Structure: Document ID required
  • Incompatible combination of params and filter
  • Using listener with tokens is not supported in browsers
  • Schema type is missing a required property
  • API versioning in Javascript Client
  • Upgrade version of studio package
  • Slug: `slugifyFn` renamed
  • Renamed plugin @sanity/date-input
  • Specify API version when using custom document list filters
  • Migration Cheat Sheet: Studio v2 to v3
  • Migrating from Studio v2
  • Function Timeout
  • Functions rate limit
  • Configure TypeGen
  • Studio v3 to v4
  • Email addresses show [email protection]

On this page

Previous

Specify API version when using custom document list filters

Next

Migrating from Studio v2

Was this page helpful?

On this page

  • Get a client configured with current studio's details
  • Get the current logged-in user
  • Accessing the document from your custom form component
  • Accessing all document types with the schema part
  • Using config:plugin-name to configure an input component plugin
  • Custom components
Help articlesLast updated January 9, 2026

Migration Cheat Sheet: Studio v2 to v3

Cheat sheet for migrating a Studio v2 to v3

Get a client configured with current studio's details

// In v2
import sanityClient from "part:@sanity/base/client"

const client = sanityClient.withConfig({apiVersion: '2021-03-25'})

// v3 in a React component

import {useClient} from 'sanity'

export function MyComponent {
  const client = useClient({apiVersion: '2021-10-21'})
  // use client as needed in, for instance in an useEffect

  return <div>JSX</div>
}

// v3 outside of React (E.g. in sanity.config.ts)

import {ConfigContext, defineConfig} from 'sanity'
import {structureTool, StructureBuilder} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig({
  name: 'default',
  title: 'sanity-v3-structure-builder',
  projectId: 'your-projectId',
  dataset: 'your-dataset',
  plugins: [
    structureTool({
      structure: (S: StructureBuilder, context: ConfigContext) => {
        const {client} = context
        
       // use client to to build the structure, for instance

        return S.defaults()
      },
    }),
  ],
  schema: {
    types: schemaTypes,
  },
})

If you use the same API version throughout your project, consider creating a reusable hook!

import {useMemo} from 'react'
import {useClient} from 'sanity'

export function useSanityClient() {
  const client = useClient()
  return useMemo(() => client.withConfig({apiVersion: '2021-10-21'}), [client])
}

//use it in a component instead of useClient
export function MyComponent {
  const client = useSanityClient()
  return <div>JSX</div>
}

Get the current logged-in user

// In v2

// v2 code
import userStore from "part:@sanity/base/user";

userStore.getCurrentUser().then((user) => {
  console.log(`[currentUser]:`, user );
})
// v3 inside a React component
import {useCurrentUser} from 'sanity'

export function MyComponent {
  const user = useCurrentUser()
  const {id, name, email, profileImage, provider, roles} = currentUser

  return <div>JSX</div>
}

// v3 outside of React, (E.g. in sanity.config.ts)

import {ConfigContext, defineConfig} from 'sanity'
import {structureTool, StructureBuilder} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig({
  name: 'default',
  title: 'sanity-v3-structure-builder',

  projectId: 'your-projectId',
  dataset: 'your-dataset',
  plugins: [
    structureTool({
      structure: (S: StructureBuilder, context: ConfigContext) => {
        const {projectId, dataset, schema, currentUser, client} = context
        // Because `currentUser` can be null, typescript won't allow 
        // destructuring I.e. ⬇ This won't fly!
        // const {id, name, email, profileImage, provider, roles} = currentUser
        // ...but ⬇ this will
        // currentUser?.id, currentUser?.name, currentUser?.email, etc...

	      const currentProfileImage = currentUser?.profileImage ?? 'https://picsum.photos/200/300';
				



        // your logic

        return S.defaults()
      },
    }),
  ],
  schema: {
    types: schemaTypes,
  },
})

Accessing the document from your custom form component

// in v2

import {withDocument} from 'part:@sanity/form-builder'

function MyInput(props) {
  return (
    <div>
      Document title: {props.document.title}
      {/* ... */}
    </div>
  )
}

export default withDocument(MyInput)
// in v3
import {useFormValue} from 'sanity'

function MyInput() {
  const title = useFormValue([`title`])
  return (
    <div>
      Document title: {title}
      {/* ... */}
    </div>
  )
}

export default MyInput

Accessing all document types with the schema part

// v2
import schema from 'part:@sanity/base/schema'

const namedSchema = schema.get('my-schema')
// v3 inside a React components
import {useSchema} from 'sanity'

export function MyComponent {
  const schema = useSchema()
  const namedSchema = schema.get('my-schema')

  return <div>JSX</div>
}

// v3 outside React (E.g. in sanity.config.ts)

export default defineConfig({
  name: 'default',
  title: 'sanity-v3-structure-builder',
  projectId: 'your-projectId',
  dataset: 'your-dataset',
  plugins: [
    structureTool({
      structure: (S: StructureBuilder, context: ConfigContext) => {
        const {schema} = context
        
       // use schema to look up types to build your structure

        return S.defaults()
      },
    }),
  ],
  schema: {
    types: schemaTypes,
  },
})

Using config:plugin-name to configure an input component plugin

// config.dist.json
{ "unit": "celsius" }

// src/config.js
import config from 'config:plugin-name'
export default { unit: config?.unit || 'celsius' }

// src/schema.js
export default {
  name: 'plugin-name.schema-type',
  inputComponent: Input,
}

// src/Input.js
import config from './config'
export default function Input(props) {
  return config.unit === 'celsius' ? <InputCelsius {...props} /> : <InputFahrenheit {...props} />
}
// src/index.tsx
type Config {
  unit: 'celsius' | 'fahrenheit'
}

export const pluginName = definePlugin<Partial<Config> | void>(userConfig => {
  const config: Config = {unit: 'celsius', ...userConfig}
  return {
    name: 'plugin-name',
    form: {
      renderInput(props, next) {
        if(props.schemaType.type?.name === 'plugin-name.schema-type') {
          return config.unit === 'celsius' ? <InputCelsius {...props} /> : <InputFahrenheit {...props} />
        }
        return next(props)
      }
    }
  }
})

Custom components

// In v2

import MyCustomStarRatingComponent from '../components'

export default {
  name: 'rating',
  type: 'number',
  title: 'How many stars?',
  inputComponent: MyCustomStarRatingComponent
}

// In v3, inside single fields

import { MyCustomStarRatingInputComponent } from '../components'

export default {
  name: 'rating',
  type: 'number',
  title: 'How many stars?',
  components: {
    input: MyCustomStarRatingInputComponent,
  }
}

// In v3, globally from the config

import { MyCustomStarRatingInputComponent } from '../components'

export default defineConfig({
  // a lot of config
  form: {
    renderInput: (props, next) => {
      if (props.schemaType.name === "rating") {
        return MyCustomStarRatingInputComponent
      }
      return next(props)
    }
  }
  
})
// In v2
import sanityClient from "part:@sanity/base/client"

const client = sanityClient.withConfig({apiVersion: '2021-03-25'})
// v3 in a React component

import {useClient} from 'sanity'

export function MyComponent {
  const client = useClient({apiVersion: '2021-10-21'})
  // use client as needed in, for instance in an useEffect

  return <div>JSX</div>
}

// v3 outside of React (E.g. in sanity.config.ts)

import {ConfigContext, defineConfig} from 'sanity'
import {structureTool, StructureBuilder} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig({
  name: 'default',
  title: 'sanity-v3-structure-builder',
  projectId: 'your-projectId',
  dataset: 'your-dataset',
  plugins: [
    structureTool({
      structure: (S: StructureBuilder, context: ConfigContext) => {
        const {client} = context
        
       // use client to to build the structure, for instance

        return S.defaults()
      },
    }),
  ],
  schema: {
    types: schemaTypes,
  },
})
import {useMemo} from 'react'
import {useClient} from 'sanity'

export function useSanityClient() {
  const client = useClient()
  return useMemo(() => client.withConfig({apiVersion: '2021-10-21'}), [client])
}

//use it in a component instead of useClient
export function MyComponent {
  const client = useSanityClient()
  return <div>JSX</div>
}
// In v2

// v2 code
import userStore from "part:@sanity/base/user";

userStore.getCurrentUser().then((user) => {
  console.log(`[currentUser]:`, user );
})
// v3 inside a React component
import {useCurrentUser} from 'sanity'

export function MyComponent {
  const user = useCurrentUser()
  const {id, name, email, profileImage, provider, roles} = currentUser

  return <div>JSX</div>
}

// v3 outside of React, (E.g. in sanity.config.ts)

import {ConfigContext, defineConfig} from 'sanity'
import {structureTool, StructureBuilder} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig({
  name: 'default',
  title: 'sanity-v3-structure-builder',

  projectId: 'your-projectId',
  dataset: 'your-dataset',
  plugins: [
    structureTool({
      structure: (S: StructureBuilder, context: ConfigContext) => {
        const {projectId, dataset, schema, currentUser, client} = context
        // Because `currentUser` can be null, typescript won't allow 
        // destructuring I.e. ⬇ This won't fly!
        // const {id, name, email, profileImage, provider, roles} = currentUser
        // ...but ⬇ this will
        // currentUser?.id, currentUser?.name, currentUser?.email, etc...

	      const currentProfileImage = currentUser?.profileImage ?? 'https://picsum.photos/200/300';
				



        // your logic

        return S.defaults()
      },
    }),
  ],
  schema: {
    types: schemaTypes,
  },
})
// in v2

import {withDocument} from 'part:@sanity/form-builder'

function MyInput(props) {
  return (
    <div>
      Document title: {props.document.title}
      {/* ... */}
    </div>
  )
}

export default withDocument(MyInput)
// in v3
import {useFormValue} from 'sanity'

function MyInput() {
  const title = useFormValue([`title`])
  return (
    <div>
      Document title: {title}
      {/* ... */}
    </div>
  )
}

export default MyInput
// v2
import schema from 'part:@sanity/base/schema'

const namedSchema = schema.get('my-schema')
// v3 inside a React components
import {useSchema} from 'sanity'

export function MyComponent {
  const schema = useSchema()
  const namedSchema = schema.get('my-schema')

  return <div>JSX</div>
}

// v3 outside React (E.g. in sanity.config.ts)

export default defineConfig({
  name: 'default',
  title: 'sanity-v3-structure-builder',
  projectId: 'your-projectId',
  dataset: 'your-dataset',
  plugins: [
    structureTool({
      structure: (S: StructureBuilder, context: ConfigContext) => {
        const {schema} = context
        
       // use schema to look up types to build your structure

        return S.defaults()
      },
    }),
  ],
  schema: {
    types: schemaTypes,
  },
})
// config.dist.json
{ "unit": "celsius" }

// src/config.js
import config from 'config:plugin-name'
export default { unit: config?.unit || 'celsius' }

// src/schema.js
export default {
  name: 'plugin-name.schema-type',
  inputComponent: Input,
}

// src/Input.js
import config from './config'
export default function Input(props) {
  return config.unit === 'celsius' ? <InputCelsius {...props} /> : <InputFahrenheit {...props} />
}
// src/index.tsx
type Config {
  unit: 'celsius' | 'fahrenheit'
}

export const pluginName = definePlugin<Partial<Config> | void>(userConfig => {
  const config: Config = {unit: 'celsius', ...userConfig}
  return {
    name: 'plugin-name',
    form: {
      renderInput(props, next) {
        if(props.schemaType.type?.name === 'plugin-name.schema-type') {
          return config.unit === 'celsius' ? <InputCelsius {...props} /> : <InputFahrenheit {...props} />
        }
        return next(props)
      }
    }
  }
})
// In v2

import MyCustomStarRatingComponent from '../components'

export default {
  name: 'rating',
  type: 'number',
  title: 'How many stars?',
  inputComponent: MyCustomStarRatingComponent
}

// In v3, inside single fields

import { MyCustomStarRatingInputComponent } from '../components'

export default {
  name: 'rating',
  type: 'number',
  title: 'How many stars?',
  components: {
    input: MyCustomStarRatingInputComponent,
  }
}

// In v3, globally from the config

import { MyCustomStarRatingInputComponent } from '../components'

export default defineConfig({
  // a lot of config
  form: {
    renderInput: (props, next) => {
      if (props.schemaType.name === "rating") {
        return MyCustomStarRatingInputComponent
      }
      return next(props)
    }
  }
  
})