Querying archived posts in Sanity.io causing unexpected results

13 replies
Last updated: May 9, 2023
So I am running into what I think is an odd issue where my query looks like this
export const allPosts = groq`*[_type == "post" && isArchived != true]{
  ${postFields}
}`
As when I inspect my post I see
isArchived:true
and I don't want to display posts that are listed as archived. However, this still shows that post. If I update the query to
export const allPosts = groq`*[_type == "post" && isArchived == false]{
  ${postFields}
}`
or even

export const allPosts = groq`*[_type == "post" && !isArchived]{
  ${postFields}
}`
the posts are hidden that are listed as archived
AI Update

This is a common gotcha with GROQ's inequality operator! The issue you're experiencing comes down to how GROQ handles null values in filter expressions.

When you use isArchived != true, here's what happens:

  • If isArchived is false, the expression evaluates to true βœ“ (document matches)
  • If isArchived is true, the expression evaluates to false (document is filtered out)
  • If isArchived is null or undefined, the expression evaluates to null

The key problem is that when a filter expression returns null, GROQ treats it as falsy and filters out that document. However, the behavior can be inconsistent or confusing because null in boolean contexts doesn't always behave as you'd expect, and the != operator specifically returns null when comparing with null values.

So isArchived != true should theoretically work, but the way null is handled in filter contexts can lead to unexpected results. This is why your working solutions are more reliable:

Option 1: Explicit false check

*[_type == "post" && isArchived == false]

This explicitly matches documents where isArchived is exactly false. Documents where the field is null or undefined will NOT match (because null == false returns null, which is falsy in filters).

Option 2: Negation operator (recommended)

*[_type == "post" && !isArchived]

This is the most idiomatic approach in GROQ. The ! operator will evaluate to true for false, null, and undefined values, so it includes all non-archived posts regardless of whether the field exists.

Option 3: Explicit null handling If you want to be very specific about which documents to include, you can use the defined() function:

*[_type == "post" && (!defined(isArchived) || isArchived == false)]

This explicitly includes posts where isArchived doesn't exist OR is false.

For most use cases with a boolean "archived" flag, !isArchived is your best bet - it's clean, readable, and treats missing values the same as false, which is typically what you want for feature flags like this.

Which API version?
Also,
isArchived != true
will also match any post that does not have the
isArchived
attribute, or where it's null.
I have
export const apiVersion = process.env.NEXT_PUBLIC_SANITY_API_VERSION || '2023-01-01'
And that is fine if it matches posts that don't have
isArchived
or it's null. However, my issue is that it was still matching posts that had it set to
true
.
I don't know what API version that is, just make sure you are using
v2022*
or
v2023*
.
That is odd, if so. This is an extremely well-tested type of query. Can you reproduce it with a single query URL or using the Vision plugin?
Oh I have always see a full date used for
apiVersion
πŸ€” even in
user G
tutorial she says you can put any date you want basically. The vision plugin works as expected but the API version shows
v2021-10-21
πŸ€”
But examples like
this show the
apiVersion
as
2022-11-15
πŸ€”
Yes, you need a full date. I mean you need an API version that starts with 2022 or 2023.
πŸ€¦β€β™‚οΈ the problem was me πŸ˜…So I have

   defineField({
      type: 'boolean',
      name: 'isArchived',
      title: 'Archive Post?',
      description: 'Archived posts will not be shown in the front-end',
      initialValue: false,
    }),
where the initial value
should be false but somehow my editor got a state where the boolean is right in the middle and that was the one that kept showing up when
isArchived !== true
but disappearing when I did
!isArchived
πŸ˜…
What would be the safer bet? Just to do
!isArchived
?
I think that's clean
Same, I just don't know how the post got into a null state as when I created a new post it was in the false state and even toggling it, it still did not get into the null state πŸ€”
If you want to handle the null state too, there's nothing wrong with
!= true
either. πŸ™‚
Yea that might be safer, my client is a senior citizen building a website for senior citizens in our city so whatever I can do to make it as bullet proof as possible I want to do.

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?