Skip to main content
Form hooks provide access to document values, form state, and field metadata within Sanity Studio. Use these hooks in custom input components and form-related UI.

useFormValue

Retrieve the value of any field in the current document by path.
function useFormValue(path: Path): unknown
path
Path
required
Array notation with segments that are either:
  • Strings representing field names
  • Numbers for array indices (simple values)
  • Objects with _key for array items (objects with keys)
Returns: The value at the specified path, or undefined if not found. Example:
import {useFormValue, type StringInputProps} from 'sanity'

function ConditionalInput(props: StringInputProps) {
  // Get value of a sibling field
  const category = useFormValue(['category'])
  
  // Get value from nested object
  const authorName = useFormValue(['author', 'name'])
  
  // Get value from array by index
  const firstTag = useFormValue(['tags', 0])
  
  // Get value from array by key
  const specificItem = useFormValue(['items', {_key: 'abc123'}])
  
  if (category !== 'article') {
    return null // Hide this input unless category is 'article'
  }
  
  return <input {...props.elementProps} />
}
Notes:
  • Must be used within a form context (inside a document editor)
  • Returns undefined for non-existent paths
  • Re-renders when the value at the path changes

useFormState

Access the complete form state including field metadata, validation, and presence.
function useFormState(options: UseFormStateOptions): FormState | null
schemaType
ObjectSchemaType
required
Root schema type for the document
documentValue
unknown
required
Current document value
comparisonValue
unknown
Value to compare against (for change tracking)
focusPath
Path
required
Currently focused field path
openPath
Path
required
Currently open field path (for dialogs/modals)
presence
FormNodePresence[]
required
Array of user presence data for collaborative editing
validation
ValidationMarker[]
required
Validation messages for all fields
readOnly
boolean
Whether the form is read-only
Returns: ObjectFormNode containing:
  • value: Current document value
  • members: Array of field/fieldset members with their state
  • validation: Validation markers
  • presence: User presence data
  • readOnly: Computed read-only state
  • hidden: Computed hidden state
  • level: Nesting level (0 for root)
Example:
import {useFormState} from 'sanity'

function DocumentMetadata() {
  const formState = useFormState({
    schemaType,
    documentValue,
    comparisonValue: null,
    focusPath: [],
    openPath: [],
    presence: [],
    validation: [],
    perspective: 'default',
    hasUpstreamVersion: false,
  })
  
  if (!formState) return null
  
  return (
    <div>
      <h3>Document Fields</h3>
      {formState.members.map((member) => {
        if (member.kind === 'field') {
          return (
            <div key={member.name}>
              {member.name}: {member.field.validation.length} errors
            </div>
          )
        }
        return null
      })}
    </div>
  )
}

useFieldActions

Access field-level actions and state (focus, hover, comments).
function useFieldActions(): FieldActionsState
Returns: Object containing:
focused
boolean
Whether this field currently has focus
hovered
boolean
Whether this field is currently hovered
actions
React.ReactNode
Rendered action buttons for this field
onMouseEnter
() => void
Handler to call when mouse enters field
onMouseLeave
() => void
Handler to call when mouse leaves field
__internal_comments
CommentContextValue
Internal comment system state
__internal_slot
React.ReactNode
Internal slot for additional UI
Example:
import {useFieldActions} from 'sanity'

function MyInput(props) {
  const {
    focused,
    hovered,
    actions,
    onMouseEnter,
    onMouseLeave
  } = useFieldActions()
  
  return (
    <div 
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
      style={{
        borderColor: focused ? 'blue' : hovered ? 'gray' : 'transparent'
      }}
    >
      <input {...props.elementProps} />
      {actions}
    </div>
  )
}

useDocumentForm

Access the document form instance with methods for patching, focusing, and managing document state.
function useDocumentForm(): DocumentFormContextValue
Returns: Object containing:
ready
boolean
Whether the form is initialized and ready
schemaType
ObjectSchemaType
Document schema type
documentValue
SanityDocument | undefined
Current document value
patch
(patches: FormPatch[]) => void
Apply patches to the document
focusPath
Path
Currently focused field path
openPath
Path
Currently open path (for modals)
setFocusPath
(path: Path) => void
Programmatically focus a field
validation
ValidationMarker[]
All validation markers for the document
Example:
import {useDocumentForm} from 'sanity'

function ValidationSummary() {
  const {validation, setFocusPath} = useDocumentForm()
  
  const errors = validation.filter(v => v.level === 'error')
  
  return (
    <div>
      <h3>{errors.length} Errors</h3>
      {errors.map((error, i) => (
        <div key={i}>
          <button onClick={() => setFocusPath(error.path)}>
            Jump to error
          </button>
          {error.message}
        </div>
      ))}
    </div>
  )
}

useGetFormValue

Get a function to retrieve form values without subscribing to changes (for use in callbacks).
function useGetFormValue(): (path: Path) => unknown
Returns: Function that takes a path and returns the current value at that path. Example:
import {useGetFormValue} from 'sanity'

function MyInput(props) {
  const getFormValue = useGetFormValue()
  
  const handleClick = () => {
    // Get current value without causing re-render
    const currentCategory = getFormValue(['category'])
    console.log('Category:', currentCategory)
  }
  
  return <button onClick={handleClick}>Log Category</button>
}
Difference from useFormValue:
  • useFormValue: Subscribes to changes, causes re-renders
  • useGetFormValue: Returns a getter function, no re-renders

Type Definitions

Path

Array representing a field path in the document:
type Path = Array<string | number | {_key: string}>
Examples:
// Top-level field
['title']

// Nested field
['author', 'name']

// Array index
['tags', 0]

// Array item by key
['blocks', {_key: 'abc123'}]

// Deeply nested
['metadata', 'seo', 'keywords', 2]

FormState

type FormState<T = Record<string, unknown>> = ObjectFormNode<T>

interface ObjectFormNode<T> {
  kind: 'object'
  value: T | undefined
  schemaType: ObjectSchemaType
  members: Array<FieldMember | FieldSetMember>
  validation: ValidationMarker[]
  presence: FormNodePresence[]
  readOnly: boolean
  hidden: boolean
  level: number
  path: Path
  // ... additional internal properties
}

FormNodePresence

interface FormNodePresence {
  user: {
    id: string
    displayName: string
    imageUrl?: string
  }
  path: Path
  sessionId: string
  lastActiveAt: string
}

ValidationMarker

interface ValidationMarker {
  level: 'error' | 'warning' | 'info'
  message: string
  path: Path
  item?: {
    message: string
    paths?: Path[]
  }
}

Best Practices

Use useFormValue for Conditional Logic

function DependentInput(props: StringInputProps) {
  const parentType = useFormValue(['type'])
  
  // Only render if parent field has specific value
  if (parentType !== 'custom') return null
  
  return <input {...props.elementProps} />
}

Use useGetFormValue in Event Handlers

function MyInput(props) {
  const getFormValue = useGetFormValue()
  
  // Don't use useFormValue here - it would cause unnecessary re-renders
  const handleSave = useCallback(() => {
    const title = getFormValue(['title'])
    // Use title in save logic
  }, [getFormValue])
  
  return <button onClick={handleSave}>Save</button>
}

Validate Against Other Fields

import {defineField} from 'sanity'

defineField({
  name: 'endDate',
  type: 'date',
  validation: (Rule) => Rule.custom((endDate, context) => {
    // Access sibling field using context.parent
    const startDate = context.parent?.startDate
    if (endDate && startDate && endDate < startDate) {
      return 'End date must be after start date'
    }
    return true
  })
})

Build docs developers (and LLMs) love