Pagination for array of references in Sanity GROQ query not working

2 replies
Last updated: Aug 19, 2023
I’m trying to implement pagination for entries that are in an array, but I can’t seem to get any results.

*[_type == 'market' && siteDomain == "<http://www.website.se|www.website.se>"][0] {
  'tripCollections': tripCollections[@->nameSv in ["Spain"]] -> {
    'name': @.['nameSv'],
    'trips': trips[_id > "trip-17058401-3baa-40cf-8fc9-11869112b117"] | order(_id) [0...3] -> {
      _id,
      recipe,
      'images': images[]->image.asset->url,
      'headline': contentSv->headline,
      'salesPitch': contentSv->salesPitch,
      'slug': contentSv->slug
    }
  }
}

trips[_id > "trip-17058401-3baa-40cf-8fc9-11869112b117"] | order(_id) [0...3]
contains the _id of the very first item in the array of elements, then I try to fetch 0 to 3.
But for some reason I just get an empty result.
Is there anything obvious that I’m doing wrong?

If it wasn’t clear already,
trips
is an array. Also not sure if it matters but the order of the elements are important to me, I wish to preserve them the way they are in listed in the CMS.
Also important to note that the array is an array of references
AI Update

I can see the issue with your query! When you're trying to paginate through an array of references with trips[_id > "trip-17058401-3baa-40cf-8fc9-11869112b117"], the problem is that you're filtering on _id before dereferencing with ->.

At that point in the query, trips is still an array of reference objects (like {_ref: "trip-123", _type: "reference"}), not the actual trip documents. Reference objects don't have an _id field—they have a _ref field. So your filter isn't matching anything, which is why you're getting empty results.

Here are two ways to fix this:

Option 1: Filter after dereferencing (simpler but less efficient)

*[_type == 'market' && siteDomain == "www.website.se"][0] {
  'tripCollections': tripCollections[@->nameSv in ["Spain"]] -> {
    'name': @.['nameSv'],
    'trips': trips[]-> {
      _id,
      recipe,
      'images': images[]->image.asset->url,
      'headline': contentSv->headline,
      'salesPitch': contentSv->salesPitch,
      'slug': contentSv->slug
    } | order(_id) [select(_id > "trip-17058401-3baa-40cf-8fc9-11869112b117")] [0...3]
  }
}

This dereferences all trips first, then filters and slices. However, this isn't ideal for performance since it fetches all trips before filtering.

Option 2: Use _ref for filtering (more efficient)

*[_type == 'market' && siteDomain == "www.website.se"][0] {
  'tripCollections': tripCollections[@->nameSv in ["Spain"]] -> {
    'name': @.['nameSv'],
    'trips': trips[_ref > "trip-17058401-3baa-40cf-8fc9-11869112b117"][0...3] -> {
      _id,
      recipe,
      'images': images[]->image.asset->url,
      'headline': contentSv->headline,
      'salesPitch': contentSv->salesPitch,
      'slug': contentSv->slug
    }
  }
}

This filters on _ref before dereferencing, which is more efficient.

Important note about order preservation

You mentioned that the order of elements is important and you want to preserve them as listed in the CMS. Be careful with | order(_id)—this will sort by document ID, not by the original array order!

If you want to preserve the original order from the CMS, simply remove the order(_id) part entirely. Arrays in Sanity maintain their order, so just slice without sorting:

'trips': trips[_ref > "trip-17058401-3baa-40cf-8fc9-11869112b117"][0...3] -> {
  // your fields
}

This way you'll get the trips in the exact order they appear in your CMS, starting from the reference after your cursor ID and taking the next 3 items.

Show original thread
2 replies
OK, I figured out what the problem was. The problem was that I had to do pagination based on the
_ref
property instead of
_id
, because
_id
only appears after the dereference.
Here’s the updated query if anyone’s interested.

*[_type == 'market' && siteDomain == "<http://www.website.se|www.website.se>"][0] {
  'tripCollections': tripCollections[@->nameSv in ["Spain"]] -> {
    'name': @.['nameSv'],
    'trips': trips[_ref > "trip-17058401-3baa-40cf-8fc9-11869112b117"] | order(_ref) [0...3] -> {
      recipe,
      'images': images[]->image.asset->url,
      'headline': contentSv->headline,
      'salesPitch': contentSv->salesPitch,
      'slug': contentSv->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?