Accessing nested array objects from GROQ query with conditional fallback

23 replies
Last updated: Jan 28, 2026
I am having a hard time reaching some objects in an array produced by a groq query. Maybe I need to write the query different, or find the object differently???
First, I have a schema for products. It looks something like this (simplified, of course):


name: "shopifyProduct",
  title: "Product",
  type: "document",
  fields: [
      {
      name: "title",
      title: "Title",
      type: "string",
    },
    {
      name: "descriptions",
      title: "Descriptions",
      type: "array",
      of: [
        { type: "descriptionShopify" },
        { type: "descriptionShortProduct" },
        { type: "descriptionSEO" },
      ],
    },
    {
      name: "images",
      title: "Images",
      description: "the first image will be displayed as the main product image in this site",
      type: "array",
      of: [{ type: "reference", to: [{ type: "shopifyImage" }] }],
    },
]}
Then, I have the groq query:


const queryString = `*[_id in ['${referenceArray.join("','")}']]{
    ...,
    descriptions[]{
      _type == 'descriptionShortProduct' => { "shortDesc": description},
      _type == 'descriptionShopify' => { "shopifyDesc": description }
    },
    "productImage": images[0]-> {...}    
  }`;
Which then creates this:


descriptions: 
    0: shopifyDesc:
        0: {_key: '7559c0a5afdb', _type: 'block', children: Array(1), markDefs: Array(0), style: 'normal'}
        1: {_key: '3723464efeb5', _type: 'block', children: Array(1), markDefs: Array(0), style: 'normal'}
        2: {_key: 'bc1f9950ce6f', _type: 'block', children: Array(3), markDefs: Array(1), style: 'normal'}
    1: shortDesc: 
        0: {_key: '342fd865500c', _type: 'block', children: Array(1), markDefs: Array(0), style: 'normal'}
images: (5) [{…}, {…}, {…}, {…}, {…}]
productImage: {_createdAt: '2022-02-15T22:19:24Z', _id: 'loems-14084081090700', _rev: 'ysQBVdnoEMKFWYdngqdrGZ', _type: 'shopifyImage', _updatedAt: '2022-02-15T22:19:46Z', …}
title: "Miles and Jax"
What I would like to do is grab the short description if it exists. If it doesn't, then grab the shopify description.
AI Update

Looking at your query structure, I can see the issue - your projection is creating an array of objects with conditional keys, which makes accessing the data awkward. The good news is there are better ways to handle this in GROQ!

The Problem

Your current query structure creates nested objects within an array:

descriptions[]{
  _type == 'descriptionShortProduct' => { "shortDesc": description},
  _type == 'descriptionShopify' => { "shopifyDesc": description }
}

This gives you an array where each element is either {shopifyDesc: [...]} or {shortDesc: [...]}, making it hard to access the data you want.

The cleanest approach is to use GROQ's coalesce() function, which returns the first non-null value from its arguments:

const queryString = `*[_id in ['${referenceArray.join("','")}']]{
  ...,
  "description": coalesce(
    descriptions[_type == 'descriptionShortProduct'][0].description,
    descriptions[_type == 'descriptionShopify'][0].description
  ),
  "productImage": images[0]-> {...}    
}`;

This gives you a single description field that automatically falls back from short description to Shopify description.

Solution 2: Filter the Array First

If you need access to both descriptions separately, filter the array to find specific types:

const queryString = `*[_id in ['${referenceArray.join("','")}']]{
  ...,
  "shortDesc": descriptions[_type == 'descriptionShortProduct'][0].description,
  "shopifyDesc": descriptions[_type == 'descriptionShopify'][0].description,
  "productImage": images[0]-> {...}    
}`;

Then in your code:

const description = product.shortDesc || product.shopifyDesc;

Solution 3: Use select() for More Complex Logic

For more complex conditional logic, you can use select():

const queryString = `*[_id in ['${referenceArray.join("','")}']]{
  ...,
  "description": select(
    count(descriptions[_type == 'descriptionShortProduct']) > 0 => descriptions[_type == 'descriptionShortProduct'][0].description,
    descriptions[_type == 'descriptionShopify'][0].description
  ),
  "productImage": images[0]-> {...}    
}`;

The coalesce() approach is usually the cleanest since it automatically handles null/undefined values and gives you exactly what you need - one description field with a fallback chain. This is much easier to work with than trying to navigate the nested array structure your current query produces!

Show original thread
23 replies

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?