Studio

Studio React Hooks

The sanity package provides utility hooks for creating custom components in your studio, with a variety of available hooks.

useClient

  • useClient(clientOptions): SanityClient

    Returns an instance of SanityClient configured with the current project and dataset. Should be provided a configuration object specifying which API version to use for queries. Perspectives can be set by adding .withConfig({perspective: 'raw'}) to the client config.

    Parameters

    • clientOptionsSourceClientOptions

      Configuration object with an appropriate value for apiVersion.

import { useClient } from 'sanity'

export function MyComponent() {
  const [types, setTypes] = useState(undefined)
	const client = useClient({ apiVersion: '2023-01-01' }).withConfig({ perspective: 'raw'})
  
  useEffect(() => {
    async function fetchTypes() {
      const res = await client.fetch(`array::unique(*[]._type)`)
      setTypes(res)
    }
    if (!types) fetchTypes();
  }, [])

	return (
		<div>
			<h1>Types in project</h1>
				<ul>	
					{types && types.map(type => (
						<li key={type}>{type}</li>
					)}
				</ul>
		</div>
	)
}

useDataset

import { useDataset } from 'sanity'

export function MyComponent() {
	const dataset = useDataset()
	return (
		{dataset === 'production' ? <ProductionComponent /> : <StagingComponent />}
	)
}

useProjectId

import { useProjectId } from 'sanity'

export function MyComponent() {
	const pid = useProjectId()
	return (
			<h1>Project ID: {pid}</h1>
	)
}

useFormValue

  • useFormValue(path): unknown

    Returns the value of the field specified by path. Paths are built using array notation with segments that can be either strings representing field names, index integers for arrays with simple values, or objects with a _key for arrays containing objects.

    Parameters

    • pathPath

      Paths are built using array notation with segments that can be either strings representing field names, index integers for arrays with simple values, or objects with a _key for arrays containing objects. Read more about paths here.

import { useFormValue } from 'sanity'

export function MyComponent() {
	// ⬇ get value of field 'name' in object 'author'
  const authorName = useFormValue(['author', 'name'])
	// ⬇ get value of the second item in array 'tags' of type 'string'
	const secondTag = useFormValue(['tags', 1])
	// ⬇ get value of the reference with the matching key in an array of references
	const specificBook = useFormValue([ 'bibliography', {_key: '<key>'} ])

  return (
		<div>Author: {authorName}</div>
	)
}

useSchema

import { useSchema } from 'sanity'

export function MyComponent() {
  const [selectedSchema, setSelectedSchema] = useState(undefined)
	
	// ⬇ the returned value contains the complete catalog of schemas in
	// the project, as well as some neat methods for interacting with them
  const schema = useSchema()

  // ⬇ returns an array of all type names in project
  const types = schema.getTypeNames()

  const handleSelect = (type) => {
		// ⬇ contrived example to show usage of 
		// both schema.has() and schema.get()
		if(schema.has(type)) {
			setSelectedSchema(schema.get(type))
		} else {
			setSelectedSchema(undefined)
		}
  }
  // ⬇ list all types in project and display schema for selected type
  return (
    <Container>
      <Card>
        {types.map((type) => (
          <button key={type} onClick={() => handleSelect(type)}>
            {type}
          </button>
        ))}
      </Card>
      <Card>
				{selectedSchema && (
					<pre>{JSON.stringify(selectedSchema, null, 2)}</pre>
				)}
			</Card>
    </Container>
  )
}

useTemplates

  • useTemplates(): Template[]

    Returns an array of initial value templates available in the project. Note that all document types have an initial value template associated that sets the value of _type, even if no templates have been configured by user.

import { useTemplates } from 'sanity'

export function MyComponent() {
  const templates = useTemplates()

  return (
    <ul>
      {templates.map((template) => (
        <li key={template.id}>
          <h1>{template.title}</h1>
          <h2>Type: {template.schemaType}</h2>
        </li>
      ))}
    </ul>
  )
}

useTools

import { useTools } from 'sanity'

export function MyComponent() {
  const tools = useTools();
  
	return (
		<div>
			<h1>Studio Tools</h1>
		  <ul>
				{tools.map(tool => <li key={tool.name}>{tool.title}</li>)}
			</ul>
		</div>
		)
}

useWorkspace

import { useWorkspace } from 'sanity'
			
export function MyComponent() {
  const { currentUser: { name }, dataset } = useWorkspace();
	return (
		 <h1>Hello, {name}! You are currently working in {dataset}!</h1>
	)
}

useDocumentStore

import {useMemo} from 'react'
import {useObservable} from 'react-rx'

const INITIAL_STATE = []

export function MyComponent() {
  const docId = useFormValue(['_id'])
  const documentStore = useDocumentStore();
  const observable = useMemo(() => 
    documentStore.listenQuery(
      `*[_type == 'article' && references($currentDoc) && !(_id in path("drafts.**"))]`,
      {currentDoc: docId},
      {}
    )
  , [documentStore, docId]);
  const results = useObservable(observable, INITIAL_STATE);  

	return (
	  /** Render component */
	)
}

Common gotchas

useClient with .withConfig()

The useClient hook returns a memoized client instance, but calling .withConfig() on it creates a new object on every render. If you add the result to a useEffect dependency array, the effect runs every render, causing an infinite loop.

Wrap the configured client in useMemo to maintain a stable reference:

import {useClient} from 'sanity'
import {useMemo, useEffect, useState} from 'react'

function MyComponent() {
  const baseClient = useClient({apiVersion: '2025-02-19'})
  const client = useMemo(
    () => baseClient.withConfig({perspective: 'raw'}),
    [baseClient]
  )

  const [data, setData] = useState(null)

  useEffect(() => {
    client.fetch('*[_type == "post"][0..9]').then(setData)
  }, [client])

  return <div>{JSON.stringify(data)}</div>
}

Complete hook reference

This page documents the most commonly used Studio hooks. For the complete list of all available hooks with full type signatures, see the Studio API reference.

Was this page helpful?