Ruhrpott - Ruhrpott's studio setup

January 14, 2023

ruhrpott blog post ruhrpott blog post

We are using Sanity Studio v3 for the Ruhrpott website. Before you read this blogpost, I'd recommend reading the Getting Started section on

Studio Configuration

The complete studio configuration for the Ruhrpott project, sanity.config.jsx, is shown below. Please read the Sanity Studio Configuration docs for a thorough explanation. Note the file extension .jsx. This is necessary since we define some jsx in this file.

import { defineConfig } from 'sanity';
import { deskTool } from 'sanity/desk';
import { ruhrpottStructure } from './deskStructure';
import { schemaTypes } from './schemas';
import { codeInput } from '@sanity/code-input';
import { dashboardTool } from "@sanity/dashboard";
import { netlifyWidget } from "sanity-plugin-dashboard-widget-netlify";
import {Card, Text} from '@sanity/ui';
import { useLayoutEffect } from 'react';
import { FiHome } from 'react-icons/fi';

const returnHomeTool = () => {
  return {
    title: 'Return Home',
    name: 'home', // localhost:3333/my-custom-tool
    icon: FiHome,
    component: (props) => {
      useLayoutEffect(() => {
        window.location.href = '';
      }, [])
      return (
      <Card padding={4}>
        <Text>Returning Home</Text>

export default defineConfig({
  name: 'default',
  title: 'ruhrpott-studio',
  projectId: '<Sanity Project ID',
  dataset: 'production',
  plugins: [
      structure: ruhrpottStructure,
      widgets: [
            title: 'Sanity Netlify deploys',
            sites: [
                title: 'Website',
                apiId: '<Netlify App ID>',
                buildHookId: '<Netlify Build Hook ID>',
                name: 'ruhrpott-sanity-website',
                url: '',

  tools: [returnHomeTool()],
  schema: {
    types: schemaTypes,

Here we import the user UI structure from deskStructure.js and the content schemas. We also add the Netlify deploy widget to the Dashboard. With it, we can trigger a site rebuild directly from the studio dashboard. This little trick is necessary since we are using an iframe to show the studio at /admin of the Ruhrpott site.

Studio UI

We are using various content types for this website.

  • Settings
  • Data sources for sections on pages
  • Pages


We use two setting files, one for global site settings and one to define the site navigation. Both are singleton content types, meaning a content type for non-repeatable data. Unlike a content type for blogposts, which is used to create many blogpost files, we'll need only one file for the site navigation. Note that there is no edit icon next to Setting Documents.

Data sources for sections on pages

We use content types to define content for Ruhrpott Football clubs, cities, and also for blog authors. This data will be used to build page sections. Note the edit icon next to All Authors. Clicking the icon will create a new empty author file.


There are two content types to build pages, pages and blogposts. These content types define a page, its metadata, and the various sections used to compose the page body. Note the edit icon next to Blog Posts. Clicking the icon will create a new empty blogpost file.

The studio UI shown above is defined with the Structure Builder API. Its definition can be found in the file deskStructure.js. Please read Introduction to Structure Builder for a thorough explanation of this subject.

import {FiSettings, FiMenu, FiUsers, FiGlobe, FiDribbble, FiTool} from "react-icons/fi";

export const ruhrpottStructure = (S) =>
      // Add the settings documents first
          .title('Setting Documents')
              .title('Site Settings')
      // add the data content types second
        // Create a list of all authors
        S.documentList().title('All Authors').filter('_type == "authors"')
      S.listItem().title('Ruhrpott Cities').icon(FiGlobe).child(
        // Create a list of all posts
        S.documentList().title('All Ruhrpott Cities').filter('_type == "cities"')
      S.listItem().title('Football Clubs').icon(FiDribbble).child(
        // Create a list of all football clubs
        S.documentList().title('All Football Clubs').filter('_type == "footballClubs"')
      // Remove all singletons and previously defined list items from the main list
      // at this point, only blog posts and pages remain to be listed
        (listItem) => !['siteSettings','navigation','authors','cities','footballClubs' ].includes(listItem.getId())


Schemas describe the site content. They are written in plain javascript. You can learn all about them here.

For the Ruhrpott project, I organize them into category folders: contentTypes, pageSections, sectionBlocks, and elements. They are combined in an index file and imported into sanity.config.jsx.