Let's talk about the second type of schema: Object. Compared to Documents, objects can't really live on their own - they are intended to be used inside of Documents.
Objects don't contain any of that metadata that Documents have, and we would need to access them via Documents.
Creating another document
To show how objects work, we're first going to make a new document for Cats. It looks the exact same as our Dog schema, with just a different name and title.
export default { name: 'cat', type: 'document', title: 'Cat', fields: [ { name: 'name', type: 'string', title: 'Name', }, { name: 'breed', type: 'string', title: 'Breed', }, { name: 'age', type: 'number', title: 'Age', }, ],}
Remember to add it to our array of schema types, and save these files.
import dog from './dog'import cat from './cat'
export const schemaTypes = [dog, cat]
You should now see the Cat document schema show up in Sanity Studio underneath the Dog we made prior.
Now, let's get the whole object thing sorted out.
Making our first object
It should be obvious that these two documents share all the same fields - this could get monotonous if we wanted to make documents for Fish, Hamsters, or Parrots.
When it comes to similarities, both Age and Name are common between all of these Pets! (We won't look at Breed for now, and exclude it from this object conversation.)
Wouldn't it be easy if we could have one field that encapsulated all these other Pet-related fields? Well, that's a part of why objects exist!
First, let's create a folder /schemas/objects
. Here, create a file called petInfo.js
. Then, let's add the following code to create an object for general pet information:
export default { name: 'petInfo', type: 'object', title: 'Pet Information', fields: [ { name: 'name', type: 'string', title: 'Name', }, { name: 'age', type: 'number', title: 'Age', }, ],}
You can see our name
and age
fields, exactly the same from before - the only difference seems to be that the type is now a object
instead of a document
.
Now, let's go back to our Dog and Cat document schemas and update them to use this new object:
export default { name: 'dog', type: 'document', title: 'Dog', fields: [ { name: 'petInfo', type: 'petInfo', title: 'Pet Information', }, { name: 'breed', type: 'string', title: 'Breed', }, ],}
export default { name: 'cat', type: 'document', title: 'Cat', fields: [ { name: 'petInfo', type: 'petInfo', title: 'Pet Information', }, { name: 'breed', type: 'string', title: 'Breed', }, ],}
You can see that we replaced the name
and age
fields with a new field of type petInfo
, the type we gave to this object! Go back to Sanity Studio and you'll notice...there's a problem.

We have to add the object to the schema types array:
import dog from './dog'import cat from './cat'import petInfo from './objects/petInfo'
export const schemaTypes = [dog, cat, petInfo]
Now, let's see what our Dog documents from before look like now. You can see that our object is clearly bounded, and it contains both a name and age field:

But they're empty. And there's this warning:

Let's talk this through; When we added the petInfo
object to the Dog documents, we reorganized how the fields are stored. Rather than be stored directly inside the Dog schema, it's now stored inside of the Pet Info schema, which is stored inside the Dog schema.

And since Sanity can't find our old Name and Age fields, it gives us the option to get rid of the data (since there aren't any fields that match.) If you for some reason kept those fields along with the Pet Info field, it would still work! We nested another Name and Age within Pet Info.
Go ahead and remove those fields in the warning to remove it. Also, update your Dog documents Pet Information, since that will be blank.
To further show how this works, let's look at the data that we recieve with GROQ and Vision.
The effects of objects
Let's use the same query from before in Vision:
*[_type == "dog"] { age, breed, name,}
This time, we don't get the same results:
[…] 2 items 0:{…} 3 properties age:null breed:Shih Tzu name:null 1:{…} 3 properties age:null breed:Shih Tzu name:null
age
and name
came up null
, meaning they don't exist. Now, try this query, and you'll see something different:
*[_type == "dog"] { petInfo, breed,}
[…] 2 items 0:{…} 2 properties breed:Shih Tzu petInfo:{…} 3 properties _type:petInfo age:5 name:Astro 1:{…} 2 properties breed:Shih Tzu petInfo:{…} 3 properties _type:petInfo age:3 name:Bean
You'll see that name and age are stored inside of petInfo
. What makes GROQ so amazing (that we'll go more in-depth on) is that we can create a query like so:
*[_type == "dog"] { "age": petInfo.age, breed, "name": petInfo.name,}
And we get the same data result as before, when we didn't have the petInfo
object.
[…] 2 items 0:{…} 3 properties age:5 breed:Shih Tzu name:Astro 1:{…} 3 properties age:3 breed:Shih Tzu name:Bean