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
Studio
Overview

  • Setup and development

    Installation
    Project Structure
    Development
    Hosting and deployment
    Embedding Sanity Studio
    Upgrading Sanity Studio
    Environment Variables
    Using TypeScript in Sanity Studio
    Understanding the latest version of Sanity

  • Configuration

    Introduction
    Workspaces
    Schema and forms
    Conditional fields
    Field Groups
    List Previews
    Connected Content
    Validation
    Initial Value Templates
    Cross Dataset References
    Sort Orders
    Visual editing and preview
    Incoming reference decoration

  • Block Content (Portable Text)

    Introduction
    Configure the Portable Text Editor
    Customize the Portable Text Editor
    Create a Portable Text behavior plugin
    Add Portable Text Editor plugins to Studio
    Common patterns
    Standalone Portable Text Editor

  • Studio customization

    Introduction
    Custom component for Sanity Studio
    Custom authentication
    Custom asset sources
    Diff components
    Form Components
    How form paths work
    Icons
    Favicons
    Localizing Sanity Studio
    New Document Options
    Studio Components
    Studio search configuration
    Focus and UI state in custom inputs
    Real-time safe patches for input components
    Sanity UI
    Studio Tools
    Create a custom Studio tool
    Tools cheat sheet
    Theming

  • Workflows

    The Dashboard tool for Sanity Studio
    Add widgets to dashboard
    Document actions
    Release Actions
    Custom document badges
    Localization
    Content Releases Configuration
    Enable and configure Comments
    Configuring Tasks
    Scheduled drafts
    Scheduled publishing (deprecated)
    Manage notifications

  • Structure builder

    Introduction
    Get started with Structure Builder API
    Override default list views
    Create a link to a single edit page in your main document type list
    Manually group items in a pane
    Dynamically group list items with a GROQ filter
    Create custom document views with Structure Builder
    Cheat sheet
    Structure tool
    Reference

  • Plugins

    Introduction
    Installing and configuring plugins
    Developing plugins
    Publishing plugins
    Internationalizing plugins
    Reference
    Official plugins repo

  • AI Assist

    Installation
    Translation
    Custom field actions
    Field action patterns

  • User guides

    Comments
    Task
    Copy and paste fields
    Compare document versions
    Content Releases
    Scheduled drafts
    View incoming references
    Common keyboard shortcuts

  • Studio schema reference

    Studio schema configuration
    Array
    Block
    Boolean
    Cross Dataset Reference
    Date
    Datetime
    Document
    File
    Geopoint
    Global Document Reference
    Image
    Number
    Object
    Reference
    Slug
    Span
    String
    Text
    URL

  • Studio reference

    Asset Source
    Configuration
    Document
    Document Badges
    Document Actions
    Form
    Form Components
    Hooks
    Structure tool
    Studio Components Reference
    Tools
    Initial Value Templates
    Studio API reference

On this page

Previous

Custom field actions

Next

Comments

Was this page helpful?

On this page

  • Basic setup
  • Auto-fill any field
  • Fill document from user input
  • Generate image from user input
  • Summarize the document to a field
  • Fix spelling
  • Translate to a user-defined language
StudioLast updated October 28, 2025

Field action patterns

Examples and best practices for using AI Assist custom field actions.

This is a paid feature

This feature is available in the Growth plan.

AI Assist custom field actions allow you to hook into the AI assist menu to add your own programmatic actions.

Prerequisites:

  • Install and configure AI Assist in your studio.
  • Review the field action guide.
  • Get to know Agent Actions.

Basic setup

Each example below includes the field action and the config example. Set up configuration examples in your studio config. For example:

import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          // add field action setup here
        }
      }
    })
  ]
})

For each example, remember to update any paths and filenames to match your project structure. You may also need to edit individual fields and paths to fit your schema's needs.

Auto-fill any field

This action will generate a new value for the document or field it is invoked for. The value will be contextually based on what is already in the document, and use the language in a `language` field (if present).

import {type AssistFieldActionProps, defineAssistFieldAction} from '@sanity/assist'
import {useMemo} from 'react'
import {EditIcon} from '@sanity/icons'
import {pathToString, useClient} from 'sanity'

export function useAutoFillFieldAction(props: AssistFieldActionProps) {
  const {
    actionType,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
  } = props

  const client = useClient({apiVersion: 'vX'})

  return useMemo(() => {
    return defineAssistFieldAction({
      title: actionType ? 'Autofill field' : 'Autofill document',
      icon: EditIcon,
      onAction: async () => {
        await client.agent.action.generate({
          schemaId,
          targetDocument: {
            operation: 'createIfNotExists',
            _id: documentIdForAction,
            _type: documentSchemaType.name,
            initialValues: getDocumentValue(),
          },
          instruction: `
            We are generating a new value for a document field.
            The document type is ${documentSchemaType.name}, and the document type title is ${documentSchemaType.title}
            The document language is: "$lang" (use en-US if unspecified)
            The document value is:
            $doc
            ---
            We are in the following field:
            JSON-path: ${pathToString(path)}
            Title: ${schemaType.title}
            Value: $field (consider it empty if undefined)
            ---
            Generate a new field value. The new value should be relevant to the document type and context.
            Keep it interesting. Generate using the document language.
                     `,
          instructionParams: {
            doc: {type: 'document'},
            field: {type: 'field', path},
            lang: {type: 'field', path: ['language']},
          },
          target: {
            // `mixed` will append on array-like fields and set on non-array fields (the default option).
            // Optionally change this to `set` or `append` to force patch behavior across all field types.
            operation: 'mixed',
            path,
          },
          conditionalPaths: {
            paths: getConditionalPaths(),
          },
        })
      },
    })
  }, [
    client,
    actionType,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
  ])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useAutoFillFieldAction } from 'actions/autoFill'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const autoFill = useAutoFillFieldAction(props)
          return useMemo(() => {
            return [autoFill]
          }, [autoFill])
        }
      }
    })
  ]
})

Fill document from user input

This action will generate a new value for the document based on user provided input. It uses the getUserInput feature.

import {type AssistFieldActionProps, defineAssistFieldAction, useUserInput} from '@sanity/assist'
import {useMemo} from 'react'
import {ComposeIcon} from '@sanity/icons'
import {useClient} from 'sanity'

export function useFillDocumentFromInput(props: AssistFieldActionProps) {
  const {
    actionType,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    schemaId,
  } = props

  const client = useClient({apiVersion: 'vX'})
  const getUserInput = useUserInput()

  return useMemo(() => {
    if (actionType !== 'document') {
      return undefined
    }
    return defineAssistFieldAction({
      title: 'Fill document...',
      icon: ComposeIcon,
      onAction: async () => {
        const userInput = await getUserInput({
          title: 'What should the document be about?',
          inputs: [
            {
              id: 'topic',
              title: 'Instruction',
              description:
                'Describe what the document should be about. ' +
                'Feel free to provide material for that will be used to create the document.',
            },
          ],
        })

        if (!userInput) {
          return undefined // user closed the dialog
        }

        const [{result: instruction}] = userInput
        await client.agent.action.generate({
          schemaId,
          targetDocument: {
            operation: 'createIfNotExists',
            _id: documentIdForAction,
            _type: documentSchemaType.name,
            initialValues: getDocumentValue(),
          },
          instruction: `
            Populate a document in full, based on a user instruction.
            The document type is ${documentSchemaType.name}, and the document type title is ${documentSchemaType.title}
            Pay attention to the language used in the user description, and use the same language for the document content.
            ---
            The user instruction is:
            ${instruction}
            ---
                     `,
          target: {
            operation: 'set',
          },
          conditionalPaths: {
            paths: getConditionalPaths(),
          },
        })
      },
    })
  }, [
    actionType,
    client,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    getUserInput,
    schemaId,
  ])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useFillDocumentFromInput } from 'actions/fillDocumentFromInput'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const fillDocument = useFillDocumentFromInput(props)
          return useMemo(() => {
            return [fillDocument]
          }, [fillDocument])
        }
      }
    })
  ]
})

Generate image from user input

This action will generate an image based on input from the user.

import {
  type AssistFieldActionProps,
  defineAssistFieldAction,
  isType,
  useUserInput,
} from '@sanity/assist'
import {useMemo} from 'react'
import {ComposeIcon} from '@sanity/icons'
import {useClient} from 'sanity'

export function useGenerateImageFromInput(props: AssistFieldActionProps) {
  const {
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
  } = props

  const client = useClient({apiVersion: 'vX'})
  const getUserInput = useUserInput()

  return useMemo(() => {
    // only add to image fields
    if (!isType(schemaType, 'image')) {
      return undefined
    }

    return defineAssistFieldAction({
      title: 'Generate image...',
      icon: ComposeIcon,
      onAction: async () => {
        const userInput = await getUserInput({
          title: 'Describe the image',
          inputs: [
            {
              id: 'image-description',
              title: 'Image description',
            },
          ],
        })
        if (!userInput) {
          return undefined // user closed the dialog
        }

        const [{result: instruction}] = userInput

        await client.agent.action.generate({
          schemaId,
          targetDocument: {
            operation: 'createIfNotExists',
            _id: documentIdForAction,
            _type: documentSchemaType.name,
            initialValues: getDocumentValue(),
          },
          instruction: `
            Create an image based on the following instruction:
            ${instruction}
            ---
                     `,
          target: {
            operation: 'set',
            path: [...path, 'asset'],
          },
          conditionalPaths: {
            paths: getConditionalPaths(),
          },
        })
      },
    })
  }, [
    client,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    getUserInput,
    path,
    schemaId,
    schemaType,
  ])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useGenerateImageFromInput } from 'actions/generateImageFromInput'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const generateImage = useGenerateImageFromInput(props)
          return useMemo(() => {
            return [generateImage]
          }, [generateImage])
        }
      }
    })
  ]
})

Summarize the document to a field

This action reads the contents of the document, then summarizes it into the selected field.

import {type AssistFieldActionProps, defineAssistFieldAction, isType} from '@sanity/assist'
import {useMemo} from 'react'
import {EditIcon} from '@sanity/icons'
import {isArrayOfObjectsSchemaType, pathToString, SchemaType, useClient} from 'sanity'
import {useToast} from '@sanity/ui'

export function useSummarizeDocument(props: AssistFieldActionProps) {
  const {
    actionType,
    documentIdForAction,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
  } = props

  const client = useClient({apiVersion: 'vX'})
  const {push: pushToast} = useToast()

  return useMemo(() => {
    if (actionType !== 'field') {
      return undefined
    }

    const isSupportedType =
      isType(schemaType, 'string') || isType(schemaType, 'text') || isPortableTextArray(schemaType)
    if (!isSupportedType) {
      return undefined
    }

    const lastSegment = path.slice(-1)[0]
    if (
      typeof lastSegment !== 'string' ||
      !['summary', 'description'].some((contains) => lastSegment.toLowerCase().includes(contains))
    ) {
      return undefined
    }

    return defineAssistFieldAction({
      title: 'Summarize document',
      icon: EditIcon,
      onAction: async () => {
        if (!getDocumentValue()?._createdAt) {
          pushToast({
            title: 'Document is new',
            description:
              'The document is new, without meaningful content to summarize. Make an edit and try again.',
          })
          return
        }

        await client.agent.action.generate({
          schemaId,
          documentId: documentIdForAction,
          instruction: `
                        Given the following document:
                        $doc
                        ---
                        We are in the following field:
                        JSON-path: ${pathToString(path)}
                        Title: ${schemaType.title}
                        Description: ${schemaType.description ?? 'n/a'}
                        ---
                        Generate a summary of the document that is contextually relevant for the field.
                     `,
          instructionParams: {
            doc: {type: 'document'},
          },
          target: {
            operation: 'set',
            path,
          },
          conditionalPaths: {
            paths: getConditionalPaths(),
          },
        })
      },
    })
  }, [
    actionType,
    client,
    documentIdForAction,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
    pushToast,
  ])
}

export function isPortableTextArray(type: SchemaType) {
  return isArrayOfObjectsSchemaType(type) && type.of.some((t) => isType(t, 'block'))
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useSummarizeDocument } from 'actions/summarizeDocument'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const summarize = useSummarizeDocument(props)
          return useMemo(() => {
            return [summarize]
          }, [summarize])
        }
      }
    })
  ]
})

Fix spelling

This action corrects the spelling in a field, including nested fields.

import {type AssistFieldActionProps, defineAssistFieldAction} from '@sanity/assist'
import {useMemo} from 'react'
import {TranslateIcon} from '@sanity/icons'
import {useClient} from 'sanity'

export function useFixSpelling(props: AssistFieldActionProps) {
  const {documentIdForAction, getConditionalPaths, path, schemaId} = props

  const client = useClient({apiVersion: 'vX'})

  return useMemo(() => {
    return defineAssistFieldAction({
      title: 'Fix spelling',
      icon: TranslateIcon,
      onAction: async () => {
        await client.agent.action.transform({
          schemaId,
          documentId: documentIdForAction,
          instruction: 'Fix any spelling mistakes',
          // no need to send path for document actions
          target: path.length ? {path} : undefined,
          conditionalPaths: {paths: getConditionalPaths()},
        })
      },
    })
  }, [client, documentIdForAction, getConditionalPaths, path, schemaId])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useFixSpelling } from 'actions/fixSpelling'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const spelling = useFixSpelling(props)
          return useMemo(() => {
            return [spelling]
          }, [spelling])
        }
      }
    })
  ]
})

Translate to a user-defined language

This action takes user input for the language, and translates the document or field's contents to that language.

import {type AssistFieldActionProps, defineAssistFieldAction, useUserInput} from '@sanity/assist'
import {useMemo} from 'react'
import {TranslateIcon} from '@sanity/icons'
import {useClient} from 'sanity'

export function useTranslateToAny(props: AssistFieldActionProps) {
  const {documentIdForAction, getConditionalPaths, path, schemaId} = props

  const client = useClient({apiVersion: 'vX'})
  const getUserInput = useUserInput()

  return useMemo(() => {
    return defineAssistFieldAction({
      title: 'Translate to language...',
      icon: TranslateIcon,
      onAction: async () => {
        const userInput = await getUserInput({
          title: 'Translate',
          inputs: [
            {
              id: 'instruction',
              title: 'Language',
              description: 'Which language do you want to translate to?',
            },
          ],
        })

        if (!userInput) {
          return undefined // user closed the dialog
        }

        const [{result: userLanguage}] = userInput

        await client.agent.action.translate({
          schemaId,
          documentId: documentIdForAction,
          // this is just fox example purposes: it is reccomended to use real language ids, not ones provided by a user string
          toLanguage: {
            title: userLanguage,
            id: userLanguage,
          },
          // no need to send path for document actions
          target: path.length ? {path} : undefined,
          conditionalPaths: {paths: getConditionalPaths()},
        })
      },
    })
  }, [client, documentIdForAction, getConditionalPaths, path, schemaId, getUserInput])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useTranslateToAny } from 'actions/translateToAny'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const translate = useTranslateToAny(props)
          return useMemo(() => {
            return [translate]
          }, [translate])
        }
      }
    })
  ]
})
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          // add field action setup here
        }
      }
    })
  ]
})
import {type AssistFieldActionProps, defineAssistFieldAction} from '@sanity/assist'
import {useMemo} from 'react'
import {EditIcon} from '@sanity/icons'
import {pathToString, useClient} from 'sanity'

export function useAutoFillFieldAction(props: AssistFieldActionProps) {
  const {
    actionType,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
  } = props

  const client = useClient({apiVersion: 'vX'})

  return useMemo(() => {
    return defineAssistFieldAction({
      title: actionType ? 'Autofill field' : 'Autofill document',
      icon: EditIcon,
      onAction: async () => {
        await client.agent.action.generate({
          schemaId,
          targetDocument: {
            operation: 'createIfNotExists',
            _id: documentIdForAction,
            _type: documentSchemaType.name,
            initialValues: getDocumentValue(),
          },
          instruction: `
            We are generating a new value for a document field.
            The document type is ${documentSchemaType.name}, and the document type title is ${documentSchemaType.title}
            The document language is: "$lang" (use en-US if unspecified)
            The document value is:
            $doc
            ---
            We are in the following field:
            JSON-path: ${pathToString(path)}
            Title: ${schemaType.title}
            Value: $field (consider it empty if undefined)
            ---
            Generate a new field value. The new value should be relevant to the document type and context.
            Keep it interesting. Generate using the document language.
                     `,
          instructionParams: {
            doc: {type: 'document'},
            field: {type: 'field', path},
            lang: {type: 'field', path: ['language']},
          },
          target: {
            // `mixed` will append on array-like fields and set on non-array fields (the default option).
            // Optionally change this to `set` or `append` to force patch behavior across all field types.
            operation: 'mixed',
            path,
          },
          conditionalPaths: {
            paths: getConditionalPaths(),
          },
        })
      },
    })
  }, [
    client,
    actionType,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
  ])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useAutoFillFieldAction } from 'actions/autoFill'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const autoFill = useAutoFillFieldAction(props)
          return useMemo(() => {
            return [autoFill]
          }, [autoFill])
        }
      }
    })
  ]
})
import {type AssistFieldActionProps, defineAssistFieldAction, useUserInput} from '@sanity/assist'
import {useMemo} from 'react'
import {ComposeIcon} from '@sanity/icons'
import {useClient} from 'sanity'

export function useFillDocumentFromInput(props: AssistFieldActionProps) {
  const {
    actionType,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    schemaId,
  } = props

  const client = useClient({apiVersion: 'vX'})
  const getUserInput = useUserInput()

  return useMemo(() => {
    if (actionType !== 'document') {
      return undefined
    }
    return defineAssistFieldAction({
      title: 'Fill document...',
      icon: ComposeIcon,
      onAction: async () => {
        const userInput = await getUserInput({
          title: 'What should the document be about?',
          inputs: [
            {
              id: 'topic',
              title: 'Instruction',
              description:
                'Describe what the document should be about. ' +
                'Feel free to provide material for that will be used to create the document.',
            },
          ],
        })

        if (!userInput) {
          return undefined // user closed the dialog
        }

        const [{result: instruction}] = userInput
        await client.agent.action.generate({
          schemaId,
          targetDocument: {
            operation: 'createIfNotExists',
            _id: documentIdForAction,
            _type: documentSchemaType.name,
            initialValues: getDocumentValue(),
          },
          instruction: `
            Populate a document in full, based on a user instruction.
            The document type is ${documentSchemaType.name}, and the document type title is ${documentSchemaType.title}
            Pay attention to the language used in the user description, and use the same language for the document content.
            ---
            The user instruction is:
            ${instruction}
            ---
                     `,
          target: {
            operation: 'set',
          },
          conditionalPaths: {
            paths: getConditionalPaths(),
          },
        })
      },
    })
  }, [
    actionType,
    client,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    getUserInput,
    schemaId,
  ])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useFillDocumentFromInput } from 'actions/fillDocumentFromInput'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const fillDocument = useFillDocumentFromInput(props)
          return useMemo(() => {
            return [fillDocument]
          }, [fillDocument])
        }
      }
    })
  ]
})
import {
  type AssistFieldActionProps,
  defineAssistFieldAction,
  isType,
  useUserInput,
} from '@sanity/assist'
import {useMemo} from 'react'
import {ComposeIcon} from '@sanity/icons'
import {useClient} from 'sanity'

export function useGenerateImageFromInput(props: AssistFieldActionProps) {
  const {
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
  } = props

  const client = useClient({apiVersion: 'vX'})
  const getUserInput = useUserInput()

  return useMemo(() => {
    // only add to image fields
    if (!isType(schemaType, 'image')) {
      return undefined
    }

    return defineAssistFieldAction({
      title: 'Generate image...',
      icon: ComposeIcon,
      onAction: async () => {
        const userInput = await getUserInput({
          title: 'Describe the image',
          inputs: [
            {
              id: 'image-description',
              title: 'Image description',
            },
          ],
        })
        if (!userInput) {
          return undefined // user closed the dialog
        }

        const [{result: instruction}] = userInput

        await client.agent.action.generate({
          schemaId,
          targetDocument: {
            operation: 'createIfNotExists',
            _id: documentIdForAction,
            _type: documentSchemaType.name,
            initialValues: getDocumentValue(),
          },
          instruction: `
            Create an image based on the following instruction:
            ${instruction}
            ---
                     `,
          target: {
            operation: 'set',
            path: [...path, 'asset'],
          },
          conditionalPaths: {
            paths: getConditionalPaths(),
          },
        })
      },
    })
  }, [
    client,
    documentIdForAction,
    documentSchemaType,
    getConditionalPaths,
    getDocumentValue,
    getUserInput,
    path,
    schemaId,
    schemaType,
  ])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useGenerateImageFromInput } from 'actions/generateImageFromInput'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const generateImage = useGenerateImageFromInput(props)
          return useMemo(() => {
            return [generateImage]
          }, [generateImage])
        }
      }
    })
  ]
})
import {type AssistFieldActionProps, defineAssistFieldAction, isType} from '@sanity/assist'
import {useMemo} from 'react'
import {EditIcon} from '@sanity/icons'
import {isArrayOfObjectsSchemaType, pathToString, SchemaType, useClient} from 'sanity'
import {useToast} from '@sanity/ui'

export function useSummarizeDocument(props: AssistFieldActionProps) {
  const {
    actionType,
    documentIdForAction,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
  } = props

  const client = useClient({apiVersion: 'vX'})
  const {push: pushToast} = useToast()

  return useMemo(() => {
    if (actionType !== 'field') {
      return undefined
    }

    const isSupportedType =
      isType(schemaType, 'string') || isType(schemaType, 'text') || isPortableTextArray(schemaType)
    if (!isSupportedType) {
      return undefined
    }

    const lastSegment = path.slice(-1)[0]
    if (
      typeof lastSegment !== 'string' ||
      !['summary', 'description'].some((contains) => lastSegment.toLowerCase().includes(contains))
    ) {
      return undefined
    }

    return defineAssistFieldAction({
      title: 'Summarize document',
      icon: EditIcon,
      onAction: async () => {
        if (!getDocumentValue()?._createdAt) {
          pushToast({
            title: 'Document is new',
            description:
              'The document is new, without meaningful content to summarize. Make an edit and try again.',
          })
          return
        }

        await client.agent.action.generate({
          schemaId,
          documentId: documentIdForAction,
          instruction: `
                        Given the following document:
                        $doc
                        ---
                        We are in the following field:
                        JSON-path: ${pathToString(path)}
                        Title: ${schemaType.title}
                        Description: ${schemaType.description ?? 'n/a'}
                        ---
                        Generate a summary of the document that is contextually relevant for the field.
                     `,
          instructionParams: {
            doc: {type: 'document'},
          },
          target: {
            operation: 'set',
            path,
          },
          conditionalPaths: {
            paths: getConditionalPaths(),
          },
        })
      },
    })
  }, [
    actionType,
    client,
    documentIdForAction,
    getConditionalPaths,
    getDocumentValue,
    path,
    schemaId,
    schemaType,
    pushToast,
  ])
}

export function isPortableTextArray(type: SchemaType) {
  return isArrayOfObjectsSchemaType(type) && type.of.some((t) => isType(t, 'block'))
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useSummarizeDocument } from 'actions/summarizeDocument'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const summarize = useSummarizeDocument(props)
          return useMemo(() => {
            return [summarize]
          }, [summarize])
        }
      }
    })
  ]
})
import {type AssistFieldActionProps, defineAssistFieldAction} from '@sanity/assist'
import {useMemo} from 'react'
import {TranslateIcon} from '@sanity/icons'
import {useClient} from 'sanity'

export function useFixSpelling(props: AssistFieldActionProps) {
  const {documentIdForAction, getConditionalPaths, path, schemaId} = props

  const client = useClient({apiVersion: 'vX'})

  return useMemo(() => {
    return defineAssistFieldAction({
      title: 'Fix spelling',
      icon: TranslateIcon,
      onAction: async () => {
        await client.agent.action.transform({
          schemaId,
          documentId: documentIdForAction,
          instruction: 'Fix any spelling mistakes',
          // no need to send path for document actions
          target: path.length ? {path} : undefined,
          conditionalPaths: {paths: getConditionalPaths()},
        })
      },
    })
  }, [client, documentIdForAction, getConditionalPaths, path, schemaId])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useFixSpelling } from 'actions/fixSpelling'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const spelling = useFixSpelling(props)
          return useMemo(() => {
            return [spelling]
          }, [spelling])
        }
      }
    })
  ]
})
import {type AssistFieldActionProps, defineAssistFieldAction, useUserInput} from '@sanity/assist'
import {useMemo} from 'react'
import {TranslateIcon} from '@sanity/icons'
import {useClient} from 'sanity'

export function useTranslateToAny(props: AssistFieldActionProps) {
  const {documentIdForAction, getConditionalPaths, path, schemaId} = props

  const client = useClient({apiVersion: 'vX'})
  const getUserInput = useUserInput()

  return useMemo(() => {
    return defineAssistFieldAction({
      title: 'Translate to language...',
      icon: TranslateIcon,
      onAction: async () => {
        const userInput = await getUserInput({
          title: 'Translate',
          inputs: [
            {
              id: 'instruction',
              title: 'Language',
              description: 'Which language do you want to translate to?',
            },
          ],
        })

        if (!userInput) {
          return undefined // user closed the dialog
        }

        const [{result: userLanguage}] = userInput

        await client.agent.action.translate({
          schemaId,
          documentId: documentIdForAction,
          // this is just fox example purposes: it is reccomended to use real language ids, not ones provided by a user string
          toLanguage: {
            title: userLanguage,
            id: userLanguage,
          },
          // no need to send path for document actions
          target: path.length ? {path} : undefined,
          conditionalPaths: {paths: getConditionalPaths()},
        })
      },
    })
  }, [client, documentIdForAction, getConditionalPaths, path, schemaId, getUserInput])
}
import { defineConfig } from 'sanity'
import { assist } from '@sanity/assist'
import { useTranslateToAny } from 'actions/translateToAny'

export default defineConfig({
  // ... rest of your config
  plugins: [
    // ... rest of plugins,
    assist({
      fieldActions: {
        title: "My Actions",
        useFieldActions: (props) => {
          const translate = useTranslateToAny(props)
          return useMemo(() => {
            return [translate]
          }, [translate])
        }
      }
    })
  ]
})