👀 See Sanity in action: Watch product demo now →

Studio Components

The studio components API allows you to customize the UI of your Sanity Studio.

Introduction

The studio.components config property enables configuration-level customization of your studio. On the initial introduction of the API, the following components can be overridden:

// sanity.config.js

export default defineConfig({
  // rest of config ...
  studio: {
    components: {
      layout: MyLayout,
      logo: MyLogo,
      navbar: MyNavbar,
      toolMenu: MyToolMenu,
    }
  }
})

The props for each component available in the API include a callback function named renderDefault. As the name implies, renderDefault will render the default component. When you call renderDefault, you also pass along the props needed to render the default component. You can modify the props to your liking before passing them along. If you want to completely replace the component in question with your own markup, simply do not invoke renderDefault in your return statement.

// MyEnhancedNavbar.jsx
import { Stack, Card, Flex, Text } from '@sanity/ui'

// Adds markup and invokes renderDefault()
function MyEnhancedNavbar(props) {
  return (
    <Stack>
      <Card padding={3} tone="caution">
        <Flex justify="center">
          <Text>Important Message: Please Read!</Text>
        </Flex>
      </Card>
      <>{props.renderDefault(props)}</>
    </Stack>
  )
}

// Completely replaces default navbar
function MySuperiorNavbar() {
  return (
    <Stack>
      <Card padding={3} tone="caution">
        <Flex justify="center">
          {/* Custom navbar stuff goes here */}
        </Flex>
      </Card>
    </Stack>
  )
}

For some components, like navbar and layout, the renderDefault() method is the only prop passed along, while other components receive additional props.

function MyLogo(props) {
	// LogoProps includes the title from project config by default
  const { renderDefault, title } = props;
	// Overwrite the value of `title` after spreading the props object
  return renderDefault({...props, title: title.toUpperCase() });
}
import { isDev } from 'sanity'

function MyToolMenu(props) {
	// ToolMenuProps includes list of installed tools, and more
  const { tools, renderDefault } = props;
	// Only show the dev-tool if the isDev variable resolves to true
  const availableTools = isDev ? tools : tools.filter(tool => tool.name !== 'dev-tool')
  return renderDefault({ ...props, tools: availableTools })
}

Composing renderDefault()

The rendering of components in this API uses a middleware pattern. This means that plugin customizations are applied in a chain. Each plugin may call props.renderDefault(props) to defer to default rendering. If any component in the chain fails to call the callback function, the chain breaks.

function MyCustomLogo(props) {
  return (
    <div style={{ border: '3px solid skyblue', padding: 4 }}>
      {props.renderDefault({ ...props, title: props.title?.toUpperCase() })}
    </div>
  )
}

const myLogoPlugin = definePlugin({
  name: 'my-logo-plugin',
  studio: {
    components: {
      logo: (props) => (
        <div style={{border: '3px solid hotpink'}}>
          {props.renderDefault({...props, title: 'my improved title'})}
        </div>
      ),
    },
  },
})

export default defineConfig({
  //...rest of config
  studio: {
    components: {
      logo: MyCustomLogo,
    },
  },
})

Was this article helpful?