Maximum attribute limits and nesting depth in Sanity.io

27 replies
Last updated: Jul 14, 2020
Hey! Is there a maximum amount of types you can specify in the
of
attribute of a field with
type: array
? Thanks!
AI Update

Great question! There's no specific hard limit on the number of types you can include in the of attribute of an array field. You can technically add as many different types as you need.

However, there are some important practical considerations to keep in mind:

The 2000 Attribute Limit

While you can have many types in your of array, each type contributes to your dataset's overall attribute limit of 2000 unique paths. Here's how it works:

When you have an array with multiple types, the path counting works like this:

content.list - array
content.list[] - object (counts as one path regardless of how many types)
content.list[].eachField - (this is where multiple types increase path count)

So having 5 different types in your of array doesn't directly multiply your paths by 5, but each unique field across those types does add to your total count.

Monitoring Your Attribute Usage

You can check your current attribute count by visiting:

https://<projectId>.api.sanity.io/v1/data/stats/<datasetName>

Look for the fields.count.value (it will show next to limit: 2000). Currently, you won't get warnings as you approach the limit—you'll only see errors if you exceed it—so it's good to monitor this periodically.

Other Limits to Consider

  • Maximum attribute nesting depth: 20 levels
  • Document types are multipliers: If you use the same field structure across multiple document types, each document type counts separately toward your attribute limit

Performance Considerations

While there's no hard limit on types in of, having dozens of different types might make the Studio UI a bit cumbersome for editors. Consider the user experience when deciding how many types to include.

For more details on technical limits, check out the technical limits documentation.

Hi User! I don’t think there’s an actual limit but things might get a bit cumbersome or slow at some point. However, there is an overall attribute limit of 2000 so adding tons of unique paths (each type will add to this) might bring you closer to that one. More info on limits in general: https://www.sanity.io/docs/technical-limits
You’ll probably be alright though - what’s the use case?
🙂
It’s a field that allows a list of different building blocks inside. It’s a good point what you’ve raised, as we intended to make the structure some-what recursive with restrictions
Definitely we’ll see about these 2.
Maximum number of unique attributes across all documents: 2000 attributes
Maximum attribute nesting depth: 20 levels 

Is there a way to check via CLI or Studio what is the current value of those? To see if that’s a problem before we pivot to a different content model. Does something appear on the Studio as you go over the limits, or does it only happen when importing faulty datasets? If someone is hovering over those values does Sanity contact them or what’s the way to know?
Thanks!
Regarding attribute nesting, not sure if we have any endpoint with stats on that so it’s probably good to keep an eye out for this yourself, particularly when using nested block content structures for example.
You can monitor your attribute stats via
<projectId>.<http://api.sanity.io/v1/data/stats/|api.sanity.io/v1/data/stats/><datasetName>
- check
fields.count.value
(the one right next to
limit: 2000
). You will start seeing errors if you exceed this limit but no warnings when you’re close to it. That’s something we would still like to improve since it’s helpful to get a timely warning, particularly as it relates to content modelling and it can be tricky to change everything after the fact.
Thanks!
One last thing, for attribute nesting, is there an easy way to check?Document of type… “Section”, has a field content.

section.content.alpha.bravo.charlie.delta.echo.foxtrot.golf.hotel….
that’ll be disabled to edit in Studio and/or break an import as those keys become +20?
or is there a different calculation here
I’d have to check with the team to see how the limit is enforced in this case - do you already have a path with +20 nested keys?
One of my colleagues does haha now it makes all sense. As it stopped him from importing a dataset with a JSON that ended up having…
content.0.bars.0.contentBlock.content.list.0.columns.2.contentBlock.content.list.4.columns.2.contentBlock.content.list.0.style.designTokenTypography
A bit of an edge case, and we can certainly remove a few to go back to minus 20, but certainly a flag to see if we can save some keys to avoid generating structures that are simple but still nested
Indeed, it’s probably a good opportunity to review the content model, as usually getting to the nested keys limit means you’re at risk of approaching the attribute limit as well. Sorry you’re running into this one though.
No worries! We love working with Sanity 🙌 . My final question would be… if this content structure is on a documentType1, and on a documentType 2
does it generate N attributes or does it count like the same towards the 2000 limit
assuming the final key ends up being
contentBlock.content.list.0.columns.2.contentBlock.content.list.4.columns.0.title....
, does it count as 1 attribute for
documentType1
and 1 more attribute for
documentType2
? or is the “top level document type” not part of the unique path used to calculate unique attributes
That’s a great question and one I’m not sure about. As far as I know, it looks for similar paths regardless of the enclosing document name but it would be good to put that to the test.
However, the example you give would be more than a single path even if it doesn’t differentiate between the document types. As an example:

{
  "foo": [
    {
      "bar":…,
      "baz":…
    },
    {
      "bar":…,
      "baz":…
    },
    {
      "bat": {
        "bar":…
      }
    }
  ]
}

This leads to the following six paths (attributes):

- foo -> an array
- foo[] -> an object
- foo[].bar -> a string
- foo[].baz -> a string
- foo[].bat -> an object
- foo[].bat.bar -> a string
Oh totally, that’s expected. My only concern is whether I can put
foo
fields on different documents (As a field of the top level) and still only end up with N paths…

- foo -> an array
- foo[] -> an object
- foo[].bar -> a string
- foo[].baz -> a string
- foo[].bat -> an object
- foo[].bat.bar -> a string
instead of N*M (where M is the amount of documents using it) paths

- documentType1.foo -> an array
- documentType1.foo[] -> an object
- documentType1.foo[].bar -> a string
- documentType1.foo[].baz -> a string
- documentType1.foo[].bat -> an object
- documentType1.foo[].bat.bar -> a string

- documentType2.foo -> an array
- documentType2.foo[] -> an object
- documentType2.foo[].bar -> a string
- documentType2.foo[].baz -> a string
- documentType2.foo[].bat -> an object
- documentType2.foo[].bat.bar -> a string

Fair concern! I’ll check this for you because I’m not actually sure 🙂 Will post back here tomorrow hopefully.
Thank you so much!
Hi again User, I can confirm you would end up with N*M paths in the above case, so the document types are indeed a multiplier for the number of paths I’m afraid.
Hi User, thanks for the answer.
And if
documentType2.foo[].bat
there are actually 2 different objects that could use the name
bat
(let’s imagine that internal object could have different _type, and has different fields inside), would
documentType2.foo[].bat
always count as 1 path? Obviously nested paths would vary.
Hi User, thanks so much for all the help! This is being super helpful for us.

Alternatively I’m guessing we could just create the recursion using references to a document (instead of nesting objects directly) which would certainly bring down the total possible paths (exponentially!), but will make reading this considerably harder. Although I think
_raw
fields with
resolveReferences: { maxDepth: N }
should sort it on Gatsby GraphQL and some pretty cool GROQ queries would also work alternatively.
Let’s go with a basic example:
document 0

_type: contentBlock
id: 0
content.list.0 // {} object of _type here is one of the many options available, in this case contentItemColumns
content.list.0.columns  // [] array of refs to type: contentBlock
content.list.0.columns.0._ref = 1
content.list.0.columns.1._ref = 2
document 1

_type: contentBlock
id: 1
content.list.0 // {} object of another _type here, contentItemHeading
document 2

_type: contentBlock
id: 2
content.list.0  // {} object of another _type here, contentItemImage
1.
Would you recommend a strategy like this instead of actually nesting the objects?2.
content.list.0
being a typeof (in JS)
object
, despite that on document 0 it is of Sanity
_type contentItemColumns
, on document 1 of
_type contentItemHeading
, and on document 2
_type contentItemImage
does it count as one unique attribute or does it count as 3 different? Of course that the internal fields of those 3 types would generate unique paths (for example as contentItemColumns has field
columns
and the others don’t), but there’s also an opportunity here if they all have a field called
foo
of type
string
and it counts as just 1 unique attribute instead of 3.Thanks for the patience and the great support, I think this is the last bit of information needed for us to take a second step at this and improve our modelling without ever hitting the technical limits!
Leaving an update here case in it helps anyone!
I was able to confirm array with multiple types inside (as
of
) should count as just one unique path as the path Sanity counts is…
content.list - array
content.list[] - object (it doesn't matter if N different types can be this object)
content.list[].eachField (here's where having lots of different types would start increasing the possible paths)
If you need help reviewing what paths your project is generating…
1. Install
https://www.sanity.io/docs/the-vision-plugin 2. Use the query
*
to fetch the entire dataset.3. Copy and save the entire JSON onto your disk
4. Use this package
https://www.npmjs.com/package/json-list-paths to do
jlp(yourJSON).list()
It’s a bit off (+-10-20 paths) but it gives you a pretty good understanding!
const jlp = require('json-list-paths');
const json = require('./dataset.json');
const result = jlp(json).list();
console.log(result);
console.log(`-> There were a total of ${result.length} paths in the JSON.`)

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?