Skip to content



sanity-schema-design

Author Sanity schema the way an agency ships it to editors. Covers defineType/defineField conventions, schema icons, studio folder structure, page builder block organisation, reusable field templates with editor-facing descriptions, TypeScript type generation, and turning design screenshots into schema. Use when creating or reviewing Sanity schema types, building a page builder, or scaffolding a studio.

skill 04 of 05 · MIT · v1.0.0 · updated 2026-07-28



Schema conventions for Sanity studios that editors actually enjoy using. The recurring theme: every field teaches the editor what it does, every type carries an icon, and the folder structure scales past the tenth page builder block without turning into a junk drawer.

Basic structure

For TypeScript files, always import the Sanity helpers and use them everywhere: defineType around the whole type, defineField on every field, defineArrayMember only when needed.

typescript
import { defineArrayMember, defineField, defineType } from "sanity";

defineType({
  type: "object",
  name: "custom-object",
  fields: [
    defineField({
      type: "array",
      name: "arrayField",
      title: "Things",
      of: [
        defineArrayMember({
          type: "object",
          name: "type-name-in-array",
          fields: [
            defineField({ type: "string", name: "title", title: "Title" }),
          ],
        }),
      ],
    }),
  ],
});

Always use named exports for schema types.

Icons

Every document and block type gets an icon. Prefer @sanity/icons first; fall back to whichever icon set the project already has installed (lucide-react is common). An iconless type in a page builder insert menu is unfindable once the menu passes ten entries.

Studio folder structure

Rough shape, with an index at each level so types compose into arrays near where they live:

text
studio/
├── sanity.config.ts
├── structure.ts
├── components/
│   └── slug-field-component.tsx
├── plugins/
│   └── presentation-url.ts
├── schemaTypes/
│   ├── index.ts
│   ├── common.ts
│   ├── blocks/
│   │   ├── index.ts
│   │   ├── hero.ts
│   │   ├── cta.ts
│   │   ├── faq-accordion.ts
│   │   ├── feature-cards.ts
│   │   └── image-link-cards.ts
│   ├── definitions/
│   │   ├── index.ts
│   │   ├── button.ts
│   │   ├── custom-url.ts
│   │   ├── pagebuilder.ts
│   │   └── rich-text.ts
│   └── documents/
│       ├── author.ts
│       ├── blog.ts
│       ├── faq.ts
│       └── page.ts
└── utils/
    ├── og-fields.ts
    ├── seo-fields.ts
    └── slug.ts

Do not copy these names literally; the point is the layering. documents/ for queryable document types, blocks/ for page builder sections, definitions/ for reusable field-level types.

Block index pattern

Each folder's index.ts exports an array, so registering a new block is one import plus one array entry, and the root index stays small:

typescript
import { callToAction } from "./call-to-action";
import { faqList } from "./faq-list";
import { featureCards } from "./feature-cards";
import { hero } from "./hero";
import { logoCloud } from "./logo-cloud";
import { pricingTable } from "./pricing-table";
import { richTextBlock } from "./rich-text-block";
import { statsCard } from "./stats-card";
import { testimonialQuote } from "./testimonial-quote";

export const pagebuilderBlocks = [
  hero,
  featureCards,
  faqList,
  callToAction,
  logoCloud,
  pricingTable,
  statsCard,
  testimonialQuote,
  richTextBlock,
];

export const blocks = [...pagebuilderBlocks];

Common field templates

Every field gets a name, title, description, and type, with the description written for a non-technical editor and placed above type. These templates cover the fields almost every block needs:

Eyebrow

typescript
defineField({
  name: "eyebrow",
  title: "Eyebrow",
  description: "The smaller text that sits above the title to provide context",
  type: "string",
});

Title

typescript
defineField({
  name: "title",
  title: "Title",
  description: "The large text that is the primary focus of the block",
  type: "string",
});

Heading level toggle

typescript
defineField({
  name: "isHeadingOne",
  title: "Is it a <h1>?",
  type: "boolean",
  description:
    "By default the title is a <h2> tag. If you use this as the top block on the page, you can toggle this on to make it a <h1> instead",
  initialValue: false,
});

Rich text

typescript
defineField({
  name: "richText",
  title: "Rich Text",
  description:
    "Large body of text that has links, ordered/unordered lists and headings.",
  type: "richText",
});

Buttons

typescript
defineField({
  name: "buttons",
  title: "Buttons",
  description: "Add buttons here, the website will handle the styling",
  type: "array",
  of: [{ type: "button" }],
});

Image

typescript
defineField({
  name: "image",
  title: "Image",
  type: "image",
  fields: [
    defineField({
      name: "alt",
      type: "string",
      description:
        "Remember to use alt text for people to be able to read what is happening in the image if they are using a screen reader, it's also important for SEO",
      title: "Alt Text",
    }),
  ],
});

Type generation

After adding or changing schema, regenerate TypeScript definitions:

bash
sanity schema extract && sanity typegen generate --enforce-required-fields

Run this in the same change as the schema edit so the frontend types never drift.

Building schema from screenshots

When asked to produce schema from a design screenshot, describe the types using the conventions above and lean on these visual cues:

  • Tiny text above a title is likely an eyebrow
  • Large unformatted text that reads as a header is a title or subtitle
  • Text with formatting (bold, italics, lists) needs richText
  • Every image gets an alt text field
  • Repeated button patterns use the reusable button array
  • If the project already defines richTextField or buttonsField helpers, use them

Always include editor-facing descriptions based on what the element does in the design.

About this skill

Maintained by Roboto Studio (opens in new tab), a San Francisco agency that has shipped Sanity studios for startups through enterprise. It distills the schema conventions we hold every project to. If you would rather have it done for you: robotostudio.com/services/sanity (opens in new tab).

Licensed MIT. Wow, I can't believe people are actually using these. Tell me if it worked: yo@robotostudio.com



4 more · same format, same license

Other skills

Each one is a single SKILL.md file you can read here or drop into your agent.

01

cms-to-mdx-migration

Migrate content from a headless CMS (Sanity, Contentful, WordPress) to MDX files on disk. Covers migration script architecture, resumable progress tracking, rich text to Markdown conversion gotchas, internal link resolution, frontmatter escaping, and component mapping. Use when asked to migrate off a CMS, convert structured content to MDX, or move a site to content-on-disk.

02

content-negotiation-audit

Test whether a website serves agent-readable Markdown through content negotiation. Fetches the sitemap, samples pages at random, requests each twice (Accept text/html vs text/markdown), and grades the gap across three failure tiers, no negotiation at all, leaked component placeholders in the Markdown, or content that silently goes missing (FAQ answers, tab panels, client-rendered lists). Produces a per-page scorecard and an overall verdict. Use when asked to check AI readability, Markdown parity, llms.txt setup, or how a site looks to AI crawlers and agents.

03

sanity-groq-conventions

Write maintainable GROQ for Sanity projects. Covers query file organisation, naming conventions, reusable projection fragments, parameters and localisation patterns, response typing, and query performance practices like explicit filtering and projection over whole documents. Use when writing or reviewing GROQ queries, structuring a queries file, or wiring Sanity data fetching into a frontend.

05

sitemap-audit

Audit a website's sitemap.xml to enumerate every unique page type, group URLs by pattern, and surface hidden or anomalous pages (test/staging leaks, orphaned one-offs, naming inconsistencies, pagination quirks). Use when scoping a rebuild or migration, asking "what pages does this site have", researching a competitor, or prepping a proposal where a single missed page type would derail the estimate.



Get in touch

Tell us what you're building. We reply within one working day. Jono or someone on the team picks up every message personally.

By sending this you agree to our privacy policy. We only use your details to reply.