How to reference a schema from a schema in Sanity.io

2 replies
Last updated: Feb 3, 2024
Does anyone know how you reference a schema from a schema? (i.e. self-referencing).
If a section can have children and one type of child can again be a [nested] section, how do you do that in the schema without creating a circular reference? Is this the right way?


export default defineType({
  name: "section",
  title: "Section",
  type: "object",
  fields: [

    defineField({
      type: "array",
      name: "blocks",
      title: "Content",
      of: [

        defineArrayMember(codeBlock), // <-- You can't do this with section because of circular reference
        
        defineArrayMember({ // <-- SO is this what you're supposed to do?
          name: "section",
          title: "Section",
          type: "section",
        }),

      ],
    }),

  ],

});
AI Update

Yes, exactly! The approach you're showing in your second example is the correct way to handle self-referencing schemas in Sanity. You reference the schema type by its name as a string.

Your code is spot-on:

defineArrayMember({
  name: "section",
  title: "Section",
  type: "section", // <-- This string reference prevents circular dependency issues
})

This works because Sanity's schema system resolves type references by name at runtime, not at definition time. When you use type: "section" as a string, you're telling Sanity "look up the type named 'section' when you need it" rather than trying to import and nest the actual schema object directly.

Why this works

The key difference is:

  • Importing the schema object directly (like defineArrayMember(section)) would create a circular JavaScript module dependency
  • Referencing by type name (like type: "section") lets Sanity's schema registry handle the lookup after all schemas are loaded

This pattern works for any self-referencing or mutually-referencing schemas. You can have deeply nested structures like sections within sections within sections, and Sanity will handle it gracefully.

Additional tips

  • Make sure your name property in the top-level defineType matches what you reference in the type property
  • Each array item will automatically get a _key property that Sanity uses to track items, which is especially important for recursive structures
  • You can mix self-references with other types in the same array's of array, just like you're doing with codeBlock

This is a common pattern for building flexible, nested content structures like navigation menus, nested comments, organizational hierarchies, or in your case, nested sections with blocks. The array fields documentation covers more about how arrays work with multiple member types, which applies to recursive structures as well.

This works :
export default defineConfig({
  // ...other config
  schema: {
    types: [
      defineType({
        name: 'codeBlock',
        type: 'object',
        fields: [
          defineField({
            name: 'language',
            type: 'string',
          }),
          defineField({
            name: 'code',
            type: 'text',
          }),
        ],
      }),
      defineType({
        name: 'blocks',
        type: 'array',
        of: [
          defineArrayMember({
            type: 'section',
          }),
          defineArrayMember({
            type: 'codeBlock',
          }),
        ],
      }),
      defineType({
        name: 'section',
        type: 'object',
        fields: [
          defineField({
            name: 'blocks',
            type: 'blocks',
          }),
        ],
      }),
      defineType({
        name: 'post',
        type: 'document',
        title: 'Post',
        fields: [
          defineField({
            name: 'title',
            type: 'string',
          }),
          defineField({
            name: 'content',
            type: 'blocks',
          }),
        ],
      }),
    ],
  },
})
Some things to consider though:
• You can’t have “arrays-in-array”, so you’ll have to wrap them in an object to nest
• You can’t mix object types with primitive type in an array (e.g.
type: 'reference'
and a
type: string'
)• Nested structures, and especially “infinite” nested structures can be a bit of an anti-pattern:
◦ It’s easy to blow up the
unique attribute count ◦ It can be a bit unruly to query
Thanks
user Y
!

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?