👋 Next.js Conf 2024: Come build, party, run, and connect with us! See all events

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)
    }
  }
  
})

Was this article helpful?