Managing fields in Sanity Studio: grouping and organizing schemas

11 replies
Last updated: Dec 8, 2022
Could I do something like this?
export default {
  name: 'Foo',
  type: 'document',
  fields: [title, description],
};

// And then have title and description somewhere else:

const title = {
  name: 'title',
  type: 'string',
  validation: (Rule) => Rule.required().error('The title is required.'),
};

const description = {
  name: 'description',
  type: 'text',
  validation: (Rule) => Rule.required().error('The description is required.'),
};
AI Update

Yes, absolutely! You can definitely do something like that in Sanity. This is a great pattern for creating reusable field definitions.

What you're showing is extracting field definitions into separate constants and then spreading them into your schema. This works perfectly in Sanity and is actually a recommended practice for keeping your schemas DRY (Don't Repeat Yourself).

Here's how you can do it:

// Define reusable fields
const title = {
  name: 'title',
  type: 'string',
  validation: (Rule) => Rule.required().error('The title is required.'),
};

const description = {
  name: 'description',
  type: 'text',
  validation: (Rule) => Rule.required().error('The description is required.'),
};

// Use them in your schema
export default {
  name: 'Foo',
  type: 'document',
  fields: [title, description],
};

Even better with TypeScript helpers:

If you're using TypeScript, you can use defineField for better type safety:

import {defineField, defineType} from 'sanity'

// Define reusable fields
const title = defineField({
  name: 'title',
  type: 'string',
  validation: (Rule) => Rule.required().error('The title is required.'),
});

const description = defineField({
  name: 'description',
  type: 'text',
  validation: (Rule) => Rule.required().error('The description is required.'),
});

// Use them in your schema
export default defineType({
  name: 'Foo',
  type: 'document',
  fields: [title, description],
});

Common use cases:

This pattern is especially useful when:

  • You have common fields like title, slug, publishedAt that appear across multiple document types
  • You want to maintain consistent validation rules across schemas
  • You're building a page builder with reusable field definitions
  • You need to share field configurations between related schemas

You can even organize these in a separate file like fields.ts and import them wherever needed!

I assume I could turn title and description into functions, and just call them.
Is this a good idea though?
Kind of difficult to manage the fields otherwise.
Yeah, you could turn them into functions. This is useful if you need to make schema that's mostly the same in multiple places in your Studio. You can also just set them up as their own schema files.
If they're simple schemas, though, it's likely better to just define them in the document.
That will work just fine
user Y
- I personally tend to split fields into a few groups and store each groups fields in a separate file, which works fine for simple schemas and I find it to be "neater". It could become an anti-pattern as your schemas complexity increases though
user C
Could you give a simple example of how you group fields? - I am just wondering, if you are grouping it according to type, like string, text, richText etc.
As an example, I am thinking about creating functions like this:
export const string = (name, group) => ({
  name: name,
  type: 'string',
  group: group,
  validation: (Rule) => Rule.required().error(`The ${name} is required.`),
});
Would this be a bad idea?
Not according to type, but to the fields purpose, for example:

article.ts:
export default defineType({
  name: 'article',
  type: 'document',
  groups: [
    {
      name: 'content',
      title: 'Content',
      default: true,
    },
    {
      name: 'seo',
      title: 'SEO',
    },
    {
      name: 'settings',
      title: 'Settings',
    },
  ],
  fields: [...content, ...seo, ...settings],
})
./groups/content.ts:

const group = 'content';
const content = [
    defineField({
        name: 'title',
        type: 'string',
        group,
    }),
    defineField({
        name: 'image',
        type: 'image',
        group,
    }),
    defineField({
        name: 'description',
        type: 'text',
        group,
    })
];

export default content;
The function isn't a horrible idea, but you would be giving up a lot of control over those fields, even for things like the custom error message. Depending on the context the message could require less or more specificity for example.
And if you extend the function with additional props to get back some of the control, you might end up reinventing the wheel 😄
You are right, this function could turn horribly complex if I am adding more and more parameters. 😅

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?