SvelteKit `{#each}` loop returns null when accessing array item properties

5 replies
Last updated: Feb 15, 2023
Hello everybody,I’ve got a problem looping through an array using
{#each }
in Sveltekit. Strangely it worked yesterday, I changed nothing that I’m aware of, and now it’s not working. Yay?
TL;DR

{JSON.stringify(data.homepage.sections[2]._type, null, 2)}
displays the
_type
value as expected, and looping through the array with

{#each data.homepage.sections as item }
	<section class="promo">
		<pre>{JSON.stringify(item, null, 2)}</pre>
		{item}
	</section>
{/each}
works as well.

BUT - when I try to access the individual properties within
each
, e.g.
{item._type}
, I get this error message
TypeError: Cannot read properties of null (reading '_type')
.
I’ll add more details in the thread.
AI Update

This looks like a classic issue with how GROQ queries handle arrays and null values in Sanity, rather than a SvelteKit-specific problem. When you can JSON.stringify() an item but can't access its properties, it usually means you're getting null values mixed into your array.

Here are the most likely causes and solutions:

1. Array contains null values from your GROQ query

This is the most common culprit. If your sections array has any null entries (from references that don't resolve, conditional fields, etc.), Svelte will try to iterate over them. The JSON.stringify() works because it handles null gracefully, but item._type fails when item is null.

Fix: Filter out nulls in your GROQ query or in your template:

{#each data.homepage.sections.filter(Boolean) as item}
  <section class="promo">
    {item._type}
  </section>
{/each}

Or better yet, fix it in your GROQ query by adding a filter:

*[_id == "homepage"][0] {
  sections[defined(_type)]  // or sections[!(_type == null)]
}

2. Missing projection in your GROQ query

If you're querying references or complex types, make sure you're properly dereferencing them. For example:

// Instead of just:
sections

// Use:
sections[] {
  _type,
  // other fields you need
}

The [] syntax ensures you're properly expanding array items.

3. Reactive data loading timing

Since you mentioned it "worked yesterday," this could be a timing issue where the data isn't fully loaded when the template renders. Add a safety check:

{#each data.homepage.sections ?? [] as item}
  {#if item}
    <section class="promo">
      {item._type}
    </section>
  {/if}
{/each}

4. Check your load function

Make sure your +page.server.js or +page.js is properly awaiting the Sanity query:

export async function load({ fetch }) {
  const data = await client.fetch('your query');
  return { homepage: data };
}

The fact that JSON.stringify(data.homepage.sections[2]._type) works but iterating fails strongly suggests you have null values at different indices in the array. Try logging the full array to see what's actually in there:

<pre>{JSON.stringify(data.homepage.sections, null, 2)}</pre>

My bet is you'll see some null entries mixed in with valid objects, and filtering them out (either in GROQ or in your template) will solve your issue. The SvelteKit integration with Sanity works great, but GROQ query structure is key to getting clean data.

Show original thread
5 replies
The query for the page is
import { client } from '$lib/utils/_sanity';

export const load = async () => {
	const homepage = await client.fetch(/* groq */ `
    *[ _type == "homepage"][0]{
      text,
      ctaText,
      ctaURL,
      image,
      sections[]->{
        _type == 'award' => {
          _type,
          _id,
          title,
          quoteBlock,
          quoteSigned,
          ctaText,
          ctaURL,
          image
        },
        _type == 'designspirer' => {
          _type,
          _id,
          title,
          text[]{
            ...,
            _type == "cta" => {
              ...,
              "type": @ .link->_type,
              "slug": @ .link->slug
            }
          },
          image,
          svg {
            asset-> {
              url
            }
          }
        },
        _type =='dsh' => {
          _type,
          _id,
          title,
          text,
          callOut,
          ctaText,
          ctaURL,
          image
        },
        _type =='retreat' => {
          _type,
          _id,
          subheading,
          titleLine1,
          titleLine2,
          link,
          myTags[]->{
            title
          },
          bgImage
        },
        _type =='shortcuts' => {
          _type,
          _id,
          pages[]->{
            _type,
            _id,
            name,
            title,
            slug {
              current
            }
          }
        },
        _type =='sponsor' => {
          _type,
          _id,
          title,
          showSponsor
        },
        _type =='video' => {
          _type,
          _id,
          title,
          subheading,
          text,
          ctaText,
          link->{
            slug {
              current
            }
          },
          image
        },
        _type =='workshop' => {
          _type,
          _id,
          title,
          title2,
          link,
          text,
          image
        }
      },
      "sponsors": *[_type=="company" && sponsor==true]{
        logo,
        name
      }
    }

  `);

	return {
		homepage,
	};
}

The query for the page:
The page template code:
This is the output in the browser from the first couple of objects in the array that I get from
JSON.stringify(item)
.
So each component all have
_id
and
_type
, but I can’t seem to access them at all. And this worked yesterday.
Two things that I did that made it work.1. Apparently the problem is that it was a
key
-
value
object(?), and I needed to use
Object.entries
to loop it.2. Filter away the objects with
null
value. I had one, and Svelte got stuck on it.Nr 2 I understand, but nr 1 is just pure luck and randomness from desperate searches.


{#each Object.entries(data.homepage.sections) as [i, section], index }
  {#if section !== null }
    {#if section._type === "award"}
      <PromoAward />
    {:else if section._type === "video"}
       <PromoVideo />
    {/if}
  {/if}
{/each}

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?