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

Understanding the latest version of Sanity

Next

Workspaces

Was this page helpful?

On this page

  • Minimal studio configuration example
  • Single Studio configuration
  • Multiple workspace configurations
  • Property callback functions
  • Commonly used configuration properties
  • Workspace properties
  • Schema
  • Plugins
  • Tools
  • Form
  • Document
  • Auth
StudioLast updated February 19, 2026

Configuration

Sanity Studio lets you quickly get up and running by configuring it with simple JavaScript or TypeScript.

Typically, you find the studio configuration inside a sanity.config.ts (or js) file located at the root of your project. The development server for Sanity Studio automatically picks up what's returned from the exported defineConfig function. This function takes either a single workspace configuration object or an array of configuration objects as its only argument. By implementing the pre-defined properties of this object you are able to customize a range of options and behaviors in the studio, as well as control how plugins and other studio extensions are configured.

Protip

All these are valid file suffixes for the studio configuration file: .js , .jsx , .ts , .tsx

Minimal studio configuration example

Single Studio configuration

For a single Studio configuration, the defineConfig function takes a single configuration object. The only required properties are projectId and dataset but, since this won't make for a very useful studio, we've included the structureTool()-plugin and some schemas in our example to reflect a more typical setup.

More about Schemas and Forms ->

// Single workspace configuration

import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig({
  projectId: '<projectId>',
  dataset: 'production',
  plugins: [structureTool()],
  schema: {
    types: schemaTypes,
  },
})

Multiple workspace configurations

When configuring multiple workspaces you supply an array of configuration objects. Each of these must, in addition to projectId and dataset, also include a unique basePath and name for each workspace.

More about Workspaces ->

// Multiple workspace configuration
import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig([
  {
    projectId: '<projectId>',
    dataset: 'production',
    name: 'production-workspace',
    basePath: '/production',
    title: 'Default Workspace',
    subtitle: 'production',
    plugins: [structureTool()],
    schema: {
      types: schemaTypes,
    },
  },
  {
    projectId: '<projectId>',
    dataset: 'staging',
    name: 'staging-workspace',
    basePath: '/staging',
    title: 'Another workspace',
    subtitle: 'staging',
    plugins: [structureTool()],
    schema: {
      types: schemaTypes,
    },
  },
])

Property callback functions

Many of the properties of the config object have the option of accepting a callback function instead of a static value. These callbacks are usually invoked with the previous value and a context object.

import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig({
  projectId: '<projectId>',
	dataset: 'production',
  plugins: [structureTool()],
	schema: {
    types: (prev, context) => {
      console.log(context);// logs { projectId, dataset }
      return [...schemaTypes, ...prev]
    },
  },
})

Gotcha

If you choose to use the callback function you need to make sure you return the previous value along with whatever new value you want to add. When using static values this is handled automatically by the studio.

The information included in the context object varies depending on the property in question.

import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {RocketIcon} from '@sanity/icons'
import {schemaTypes} from './schemas'

export default defineConfig({
  projectId: '<projectId>',
	dataset: 'production',
  plugins: [structureTool()],
	schema: {
    types: schemaTypes,
  },
	tools: (prev, context) => {
    console.log(context) // logs { getClient, currentUser, schema, projectId, dataset}
    return [
      {
        name: 'my-tool',
        title: 'My super-cool tool',
        icon: RocketIcon,
        component: (props) => <Card>I am a tool, albeit not a useful one</Card>,
      },
      ...prev, // remember to include previous values
    ]
  },
})

Gotcha

The example above includes some JSX in the inline component declaration. Vite, the default studio bundler, requires files that contain JSX to have a file extension or either .jsx or .tsx.

Commonly used configuration properties

Workspace properties

Every workspace configuration needs to at least include appropriate string values for dataset and projectId. If you are working with multiple workspaces in your studio, each workspace should also include a name and basePath.

//⬇ Required
dataset: 'production',
projectId: '<projectId>',
//⬇ Optional if only using a single workspace
name: 'cool-studio',  
basePath: '/my-default-workspace',
//⬇ Optional 
title: 'My Cool Studio',
subtitle: 'production'
icon: RocketIcon,

More about Workspaces ->

Schema

The schema property is where you declare your schema types. You can specify a static array of schema objects or a callback function that returns such an array.

schema: {
	types: mySchemas,
}
schema: {
  types: (prev, context) => {
    console.log(context) // logs { projectId, dataset' }
    return [...mySchemas, ...prev]
  },
},

You may also set initial value templates using the aptly named templates property. You can specify a static array of template objects or a callback function that returns such an array.

schema: {
    templates: (prev) => [
		  {
		    id: 'category-child',
		    title: 'Category: Child',
		    schemaType: 'category',
		    parameters: [{name: `parentId`, title: `Parent ID`, type: `string`}],
		    value: ({parentId}) => ({
		      parent: {_type: 'reference', _ref: parentId},
		    }),
		  },
		  {
		    id: 'article-with-author',
		    title: 'Article: Author',
		    schemaType: 'article',
		    parameters: [{name: `authorId`, title: `Author ID`, type: `string`}],
		    value: ({authorId}) => ({
		      author: authorId,
		    }),
		  },
		  ...prev,
		]
  },

More about Schemas ->

Plugins

This is where you declare plugins for your studio. It accepts a static array of plugin config objects or a callback function that returns such an array. The default studio templates come with the structureTool plugin included already.

plugins: [structureTool()],

You’ll notice that the plugin function usually needs to be invoked, not just referred to. This is because plugins, by convention, are functions that can accept configuration options as arguments.

plugins: [
    structureTool(),
    visionTool({
      defaultApiVersion: 'v2021-10-21',
      defaultDataset: 'production',
    }),
  ],

More about Plugins ->

Tools

Tools are full page-components, in that they “take over” most of the studio interface when activated, just like the structure-tool or vision plugin. Because of this behavior they also show up in your studio’s nav bar, and they can be navigated to by appending their name to your studio’s URL. E.g. https://my-cool-site.com/studio/my-tool.

Tools are declared much in the same way as plugins. The property accepts either a static array of tool configuration objects or a callback function that returns such an array.

tools: [
    {name: 'my-tool', title: 'My Tool', component: MyTool},
    {name: 'tool-2', title: '2nd Tool', component: MyOtherTool},
  ],

 // Example using the callback function with some conditional logic
  tools: (prev, {currentUser}) => {
    if (currentUser.roles.find((r) => r.name === 'admin')) {
      return [
				...prev,
				{name: 'admin', title: 'Admin', component: MyAdminTool},
			]
    }
		return prev
  },

More about Tools ->

Form

The form config property lets you configure asset sources for files and images, as well as override the default rendering of form components.

form: {
  file: {
  assetSources: myFileAssetSourceResolver,
  directUploads: true,
  }
  image: {
    assetSources: myImageAssetSourceResolver,
    directUploads: true,
  },
  components: {
    input: (props) => isStringInputProps(props) ? <MyCustomStringInput {...props} /> : props.renderDefault(props),
    field: MyCustomField,
	}
},

Gotcha

Overriding the rendering of inputs and fields in the top level studio configuration will affect all fields in your studio. If you wish to customize the rendering of only certain fields, you probably want to do so by setting the components property of the appropriate fields. More info: Introduction to Component API.

More about Asset Sources ->

More about Form Components ->

Document

This property lets you configure document actions and badges, as well as set a productionUrl for previews and specify options for new documents.

document: {
  actions: (prev) =>
    prev.map((previousAction) =>
      previousAction.action === 'publish' ? MyPublishAction : previousAction
    ),
  productionUrl: (prev, context) => {
    return `http://example.com/${context.document?.slug?.current || '404.html'}`
  },
},

More about Actions & Badges ->

Auth

This property lets you implement custom authentication by providing a configuration object that conforms to the AuthConfig signature.

import {defineConfig} from 'sanity'
/* ... */

auth: {
  redirectOnSingle: false,
  mode: 'append',
  providers: [
    {
      name: 'vandelay',
      title: 'Vandelay Industries',
      url: 'https://api.vandelay.industries/login',
      logo: '/static/img/vandelay.svg'
    }
  ],
  loginMethod: 'dual',
}

More about Authentication ->

  • Article
  • Changelog
// Single workspace configuration

import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig({
  projectId: '<projectId>',
  dataset: 'production',
  plugins: [structureTool()],
  schema: {
    types: schemaTypes,
  },
})
// Multiple workspace configuration
import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig([
  {
    projectId: '<projectId>',
    dataset: 'production',
    name: 'production-workspace',
    basePath: '/production',
    title: 'Default Workspace',
    subtitle: 'production',
    plugins: [structureTool()],
    schema: {
      types: schemaTypes,
    },
  },
  {
    projectId: '<projectId>',
    dataset: 'staging',
    name: 'staging-workspace',
    basePath: '/staging',
    title: 'Another workspace',
    subtitle: 'staging',
    plugins: [structureTool()],
    schema: {
      types: schemaTypes,
    },
  },
])
import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {schemaTypes} from './schemas'

export default defineConfig({
  projectId: '<projectId>',
	dataset: 'production',
  plugins: [structureTool()],
	schema: {
    types: (prev, context) => {
      console.log(context);// logs { projectId, dataset }
      return [...schemaTypes, ...prev]
    },
  },
})
import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {RocketIcon} from '@sanity/icons'
import {schemaTypes} from './schemas'

export default defineConfig({
  projectId: '<projectId>',
	dataset: 'production',
  plugins: [structureTool()],
	schema: {
    types: schemaTypes,
  },
	tools: (prev, context) => {
    console.log(context) // logs { getClient, currentUser, schema, projectId, dataset}
    return [
      {
        name: 'my-tool',
        title: 'My super-cool tool',
        icon: RocketIcon,
        component: (props) => <Card>I am a tool, albeit not a useful one</Card>,
      },
      ...prev, // remember to include previous values
    ]
  },
})
//⬇ Required
dataset: 'production',
projectId: '<projectId>',
//⬇ Optional if only using a single workspace
name: 'cool-studio',  
basePath: '/my-default-workspace',
//⬇ Optional 
title: 'My Cool Studio',
subtitle: 'production'
icon: RocketIcon,
schema: {
	types: mySchemas,
}
schema: {
  types: (prev, context) => {
    console.log(context) // logs { projectId, dataset' }
    return [...mySchemas, ...prev]
  },
},
schema: {
    templates: (prev) => [
		  {
		    id: 'category-child',
		    title: 'Category: Child',
		    schemaType: 'category',
		    parameters: [{name: `parentId`, title: `Parent ID`, type: `string`}],
		    value: ({parentId}) => ({
		      parent: {_type: 'reference', _ref: parentId},
		    }),
		  },
		  {
		    id: 'article-with-author',
		    title: 'Article: Author',
		    schemaType: 'article',
		    parameters: [{name: `authorId`, title: `Author ID`, type: `string`}],
		    value: ({authorId}) => ({
		      author: authorId,
		    }),
		  },
		  ...prev,
		]
  },
plugins: [structureTool()],
plugins: [
    structureTool(),
    visionTool({
      defaultApiVersion: 'v2021-10-21',
      defaultDataset: 'production',
    }),
  ],
tools: [
    {name: 'my-tool', title: 'My Tool', component: MyTool},
    {name: 'tool-2', title: '2nd Tool', component: MyOtherTool},
  ],

 // Example using the callback function with some conditional logic
  tools: (prev, {currentUser}) => {
    if (currentUser.roles.find((r) => r.name === 'admin')) {
      return [
				...prev,
				{name: 'admin', title: 'Admin', component: MyAdminTool},
			]
    }
		return prev
  },
form: {
  file: {
  assetSources: myFileAssetSourceResolver,
  directUploads: true,
  }
  image: {
    assetSources: myImageAssetSourceResolver,
    directUploads: true,
  },
  components: {
    input: (props) => isStringInputProps(props) ? <MyCustomStringInput {...props} /> : props.renderDefault(props),
    field: MyCustomField,
	}
},
document: {
  actions: (prev) =>
    prev.map((previousAction) =>
      previousAction.action === 'publish' ? MyPublishAction : previousAction
    ),
  productionUrl: (prev, context) => {
    return `http://example.com/${context.document?.slug?.current || '404.html'}`
  },
},
import {defineConfig} from 'sanity'
/* ... */

auth: {
  redirectOnSingle: false,
  mode: 'append',
  providers: [
    {
      name: 'vandelay',
      title: 'Vandelay Industries',
      url: 'https://api.vandelay.industries/login',
      logo: '/static/img/vandelay.svg'
    }
  ],
  loginMethod: 'dual',
}