Accessing nested product titles in Sanity collection array

12 replies
Last updated: Jul 5, 2022
Hi all,I'm having trouble targeting the second array layer : This is what I have at the moment

const Home = ({ product, collection }) => {
  return (
    <>
      <Collection collection={collection} />
      {(collection ?? []).map((collection) => (
        <div key={collection.title}>
          {console.log(collection.products)}</div>
      ))}
    </>
  );
};
Now I can do

{console.log(collection.products)}
this shows me the products inside the collection but my goal is to fetch the title of the products in the collection so one would say it should be like this

{console.log(collection.products.title)}
but this wont work as it returns undefined. I have a feeling there needs a second mapping for this?

This is the data I want to target
AI Update

This is a classic nested array issue! You're absolutely right that you need a second mapping. The problem is that collection.products is an array, so you can't access .title directly on it - you need to map through that array as well.

Here's how to fix it:

const Home = ({ product, collection }) => {
  return (
    <>
      <Collection collection={collection} />
      {(collection ?? []).map((collection) => (
        <div key={collection.title}>
          {/* Map through the products array */}
          {(collection.products ?? []).map((product) => (
            <div key={product._id || product.title}>
              {product.title}
            </div>
          ))}
        </div>
      ))}
    </>
  );
};

What's happening:

  1. Your first map iterates through the collection array
  2. Inside each collection, collection.products is another array of product objects
  3. You need a second .map() to iterate through each product in that array
  4. Then you can access product.title on each individual product object

A few tips:

  • I added the ?? [] safety check on collection.products too, in case a collection has no products
  • Use a unique key like product._id if available (Sanity documents have _id fields), falling back to product.title if needed
  • If you're just debugging, you could also do {console.log(collection.products[0]?.title)} to see the first product's title

If you're still getting undefined:

Make sure your GROQ query is actually fetching the product data. If products is a reference field, you need to dereference it in your query:

*[_type == "collection"] {
  title,
  products[]-> {
    title,
    _id
  }
}

The -> operator dereferences the product references so you get the actual product data, not just the reference IDs. Check out the GROQ references documentation for more details on how dereferencing works in Sanity queries.

Show original thread
12 replies
This is beside Sanity, but you need to loop through the array of products to get each products title value.
e.g


collection.products.map(product => product.title) // Will return a list of every title for each product
If you change your examle to
collection.products[0].title
then you will get the title for the first product
Hi Sindre,I know it is outside of sanity but I'm having a hard time to find answers to my questions otherwise. I'm a self learning developer thats why I try to have good input from the people here.

So thank you for interacting with me and helping me throught this.

How would this code you suggest work in this case?

collection.products.map(product => product.title)
I have tried this before

collection.products[0].title
when you try to hit title it breaks
Hi Sadiki,Don’t worry, we all started at some point
🙂Regarding your question, you need to map over your products and extract the titles that way.
But I would recommend setting meaningful variable names in your code, so your code can be understood more readily (by yourself and others).
Example:

const allProducts = collection.products || [];
const productTitles = allProducts.map(product => product.title);
when it comes to mapping to get the titles, you could do that inside of your
<Collection/>
Component, if you are displaying the data you stored in there.If you want to display it within your code here, I would recommend going about it like this:


const Home = ({ product, collection }) => {
  return (
    <>
      <Collection collection={collection} />
      {(collection ?? []).map((collectionItem) => (
        <div key={collectionItem.title}>
          {const allProducts=collection.products || [];
            allProducts.map(product => (
            <p key={product._id}>{product.title}</p>
           }
          
           </div>
      ))}
    </>
  );
};
Hope that will help you with your question
🙂
Thank you Saskia,I'm using the component to build the outcome. Let me show you what works and what dont at this point.

In my index i have a simple component like this

<Collection collectiondata={collectiondata} />
now inside that component i have this

{console.log(collectiondata[0].products[0].title)}
this logs the title of the first product in the first collection. this works fine.
Now I want to wrap it in a map so I can display all product titles that are inside the collections.

{(collectiondata ?? []).map((collectiondata) => (
      <div>
        {console.log(collectiondata.products.title)}
      </div>
    ))}
this dosnt work , I can also say I just want all the products inside the first collection like this

{console.log(collectiondata[0].products.title)}
this also dosnt work it looks very simple but somehow Im having trouble picking that data
Have you tried my map function from my previous answer?
      {(collection ?? []).map((collectionItem) => (
        <div key={collectionItem.title}>
          {const allProducts=collection.products || [];
            allProducts.map(product => (
            <p key={product._id}>{product.title}</p>
           }
          
           </div>
      ))}

But I would split the 2 map functions: the
collectiondata
in
home
and the
allProducts
in
collection
I found the problem to this. I had 2 empty collections that causes the problem. When I removed the 2 empty collections entirely it solved the errors and projected it as it should. Your method works but that seperates the products from the collection so it makes it harder to link the products to a collection. My idea was to have it all dynamicly so if there is a change somehwere or something has been added it will all dynamicly change. Thanks for looking at my code and helping me out I really appreciate it !
Wonderful you found it.If I may, maybe it would be good to setup a validation in your collection array, so it will throw a warning and keeps editors from publishing
🙂With the dynamic rendering, this still would be the case if you map your second array in the component, but it also makes your code more manageable long term.
Do you have a example for how to set up the warning properly?
Sure!As described
here , the field (array) in your Sanity Studio should look something like this:

{
  title: 'Title',
  name: 'array',
  type: 'array',
  validation: Rule => Rule.max(50).warning('Shorter lists are usually better'),
...}
Thank you Saskia !

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?