Access array item index in GROQ projection for filtered arrays

4 replies
Last updated: Jul 26, 2021
Is there a way to access the index of an item in an array in it's projection? For example:
someArray[] {
  someCondition => {
    // this is the thing I want, but I also need the next item in the list the user defined
    nextItem: someArray[currentIndex + 1]
  }

  !someCondition => {
    // mark I don't want this
    marker: true
  }
}[marker!=true]
Or, even better:

someArray[someCondition] {
  // Access the item index of the original array before I filtered it
  nextItem: [(index @ in someArray) + 1]
}
Right now it seems like the only way to do this is to deliver the entire array to my front-end and filter it client-side, which is wasteful on bandwidth.

The only bandwidth-friendly way I can do this without an index is to do multiple queries; one for the shape of the array and others for the specific data I want to fetch. But it would still be much better to do it in a single query.

// shape query
someArray[] {
  // we only get the shape of the array here
  _type, someIdentifier
}

// data query
*[someIdentifier] {...}
*[someIdentifier2] {...}
AI Update

Unfortunately, GROQ doesn't have a built-in way to access the current index of an item during array projection. There's no @index operator or similar construct that gives you the position of an element as you iterate through an array.

However, there are several workarounds depending on your use case:

Based on the Sanity Answers thread, the most practical solution is to fetch the array shape alongside your filtered data:

*[_type == "issue"][0] {
  ...articles[someCondition][0],
  "articleInfo": @ {
    ...,
    "arrayShape": ^.articles[] {
      _type, 
      slug,
      _key
    }
  }
}

This gives you the complete ordered array structure, allowing your frontend to determine the next/previous items based on the current item's identifier (_key or slug). While this requires some client-side logic, it's bandwidth-efficient since you're only fetching minimal identifying fields for the full array.

Alternative Approaches

1. Using negative slice indices:

According to the Pipeline Components documentation, GROQ supports negative indices in slices, where -1 refers to the last element:

someArray[-1]  // Last item
someArray[-2]  // Second to last
someArray[2..-1] // From third element to the last

This is useful for accessing items from the end of an array, but doesn't help you get the "next" item during projection.

2. Parent reference with the ^ operator:

Use the @ (current item) and ^ (parent) operators to reference sibling items:

*[_type == "document"] {
  "items": items[] {
    title,
    "allSiblings": ^.items[]._key
  }
}

Though accessing a specific "next" item by index from within the projection remains challenging without knowing the current position.

3. Multiple queries approach:

If you absolutely need to avoid client-side logic, you could structure your data differently or make separate queries for each item, but as you noted, this is less efficient.

Why This Limitation Exists

As noted in the community discussion, relying on array indices for content relationships is generally fragile—if the array order changes between queries, indices become unreliable. GROQ's design encourages using stable identifiers (_key, _id, slug) for relationships instead.

For your use case of linking to the next article in a user-ordered list, the array shape pattern is the most robust solution. It gives you the ordering context you need while maintaining referential integrity through unique identifiers rather than positional indices. You fetch the minimal array structure (just identifiers) alongside your detailed data, then handle the next/previous logic client-side with the stable ordering information.

This approach is bandwidth-efficient because you're only including lightweight identifiers for the full array, not the complete content of every item. Your frontend can then match the current item's _key or slug in the array shape to determine its neighbors—which is safer than relying on numeric indices that could shift between queries.

Show original thread
4 replies
Keep in mind that accessing a specific item based on a index is never safe or 100% accurate. It’s in general bad practice to use these as values or indentifiers. What are you trying to do? Why don’t create based on a unique tag/category or something which is more safe and better to use?
It allows you to do things based on how the user ordered a reorderable array (ex. preloading the next page client-side). Accessing items by index in separate queries is unsafe, but if you could (in one query) include a unique identifier to sibling items by index, then that would be safe(r)
In my case, I have an issue that has many articles, and each article needs to link on the front-end to the next article in the list. Since the ordering is user-defined there's no easy way to specify this relationship. I'd rather not force the user to click on every article and directly reference the next thing in the array.
For anyone else who has this issue, I fixed it by including the array shape in the same query as the main data; having the types for the whole array makes it more flexible anyway.
[documentFilter] {
  ...array[filterForItem][0],
  "documentInfo": @ {
     ...,
     "arrayShape": ^.array[] {
       _type, slug
     }
   }
}

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?