Skip to main content
Sanity Studio is highly customizable. You can override default components, customize the layout, modify branding, and extend functionality through configuration.

Studio component hierarchy

The studio has several levels of customization:
  • Studio components: Layout, navbar, logo, tool menu
  • Form components: Field inputs, items, previews
  • Document components: Actions, badges, inspectors

Customizing studio components

1
Import component types
2
Import the component prop types you need:
3
import {defineConfig, type LayoutProps, type NavbarProps, type LogoProps} from 'sanity'
4
Create a custom layout
5
Wrap the default layout with your own component:
6
function CustomLayout(props: LayoutProps) {
  return (
    <Box height="fill">
      {/* Add custom elements before */}
      {props.renderDefault(props)}
      {/* Add custom elements after */}
    </Box>
  )
}
7
Create a custom navbar
8
Add banners or modify the navbar:
9
import {Stack, Card, Text, Flex} from '@sanity/ui'

function CustomNavbar(props: NavbarProps) {
  return (
    <Stack>
      <Card padding={4} tone="primary">
        <Flex align="center" gap={4}>
          <Text weight="semibold" size={1}>
            Custom banner message
          </Text>
        </Flex>
      </Card>
      {props.renderDefault(props)}
    </Stack>
  )
}
10
Apply your components
11
Register them in your studio configuration:
12
export default defineConfig({
  // ... project settings
  studio: {
    components: {
      layout: CustomLayout,
      navbar: CustomNavbar,
      logo: CustomLogo,
      toolMenu: CustomToolMenu,
    },
  },
})

Component override examples

Customizing form components

You can override input components for specific field types:
import {type StringInputProps} from 'sanity'

function CustomStringInput(props: StringInputProps) {
  return (
    <Stack space={3}>
      {props.renderDefault(props)}
      <Text size={1} muted>
        Character count: {props.value?.length || 0}
      </Text>
    </Stack>
  )
}
Apply it to a specific field:
defineField({
  name: 'title',
  type: 'string',
  components: {
    input: CustomStringInput,
  },
})

Plugin-based customization

Create reusable customizations with plugins:
import {definePlugin} from 'sanity'

export const studioComponentsPlugin = definePlugin({
  name: 'studio-components-plugin',
  studio: {
    components: {
      layout: (props) => (
        <Box height="fill" data-testid="custom-layout">
          {props.renderDefault(props)}
        </Box>
      ),
      navbar: (props) => (
        <Box data-testid="custom-navbar">
          {props.renderDefault(props)}
        </Box>
      ),
    },
  },
})
Then use it in your config:
import {studioComponentsPlugin} from './plugins/studio-components'

export default defineConfig({
  plugins: [studioComponentsPlugin()],
})

Branding and theming

Customize colors, fonts, and visual styling:
import {defineConfig} from 'sanity'

export default defineConfig({
  projectId: 'your-project-id',
  dataset: 'production',
  
  theme: {
    fonts: {
      text: {
        family: 'Inter, sans-serif',
      },
      code: {
        family: 'JetBrains Mono, monospace',
      },
    },
  },
})
For advanced theming options, see the theming guide.

Customizing the workspace

Configure workspace-level settings:
export default defineConfig({
  name: 'default',
  title: 'My Studio',
  projectId: 'your-project-id',
  dataset: 'production',
  
  // Customize the base path
  basePath: '/studio',
  
  // Add custom metadata
  subtitle: 'Content management',
  
  // Configure tools
  plugins: [
    structureTool({
      icon: BookIcon,
      name: 'content',
      title: 'Content',
    }),
  ],
})

Asset source customization

Add custom asset sources for images and files:
import {defineConfig} from 'sanity'
import {unsplashAssetSource} from 'sanity-plugin-asset-source-unsplash'

export default defineConfig({
  form: {
    image: {
      assetSources: [unsplashAssetSource],
    },
  },
})

Document-level customization

Customize how documents behave:
export default defineConfig({
  document: {
    // Custom actions
    actions: (prev, context) => {
      // Filter or add actions based on document type
      if (context.schemaType === 'post') {
        return [...prev, myCustomAction]
      }
      return prev
    },
    
    // Custom badges
    badges: (prev, context) => {
      if (context.schemaType === 'author') {
        return [CustomBadge, ...prev]
      }
      return prev
    },
    
    // New document templates
    newDocumentOptions: (prev) => {
      return [...prev, {
        id: 'author-from-template',
        title: 'Author from template',
        type: 'author',
        icon: UserIcon,
      }]
    },
  },
})

Toolbar customization

Modify the document toolbar:
import {defineConfig} from 'sanity'

export default defineConfig({
  studio: {
    components: {
      unstable_formActions: (props) => {
        return (
          <>
            {props.renderDefault(props)}
            <Button text="Custom action" />
          </>
        )
      },
    },
  },
})

Using React context for state

Share state between custom components:
import {createContext, useContext} from 'react'

const StudioContext = createContext<{theme: 'light' | 'dark'}>({
  theme: 'light',
})

function CustomLayout(props: LayoutProps) {
  return (
    <StudioContext.Provider value={{theme: 'dark'}}>
      <Box height="fill">
        {props.renderDefault(props)}
      </Box>
    </StudioContext.Provider>
  )
}

function CustomNavbar(props: NavbarProps) {
  const {theme} = useContext(StudioContext)
  
  return (
    <Card tone={theme === 'dark' ? 'transparent' : 'default'}>
      {props.renderDefault(props)}
    </Card>
  )
}
Always use props.renderDefault(props) to preserve default functionality while adding your customizations.

Next steps

Build docs developers (and LLMs) love