Migration Cheat Sheet: Studio v2 to v3
Cheat sheet for migrating a Studio v2 to v3
// 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>
}
// 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)
}
}
})