GROQ: Filter posts by category when categories is an array field

6 replies
Last updated: Jan 28, 2026
For GROQ queries how do you filter posts by category if its an array
export default {
  name: 'post',
  title: 'Post',
  type: 'document',
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string',
    },
    {
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 96,
      },
    },
    {
      name: 'author',
      title: 'Author',
      type: 'reference',
      to: {type: 'author'},
    },
    {
      name: 'mainImage',
      title: 'Main image',
      type: 'image',
      options: {
        hotspot: true,
      },
    },
    {
      name: 'categories',
      title: 'Categories',
      type: 'array',
      of: [{type: 'reference', to: {type: 'category'}}],
    },
    {
      name: 'publishedAt',
      title: 'Published at',
      type: 'datetime',
    },
    {
      name: 'body',
      title: 'Body',
      type: 'blockContent',
    },
  ],

  preview: {
    select: {
      title: 'title',
      author: 'author.name',
      media: 'mainImage',
    },
    prepare(selection) {
      const {author} = selection
      return Object.assign({}, selection, {
        subtitle: author && `by ${author}`,
      })
    },
  },
}
AI Update

When filtering posts by a category field that's an array of references in GROQ, you have several options depending on what you're trying to achieve:

Basic Array Filtering

To check if a specific category reference exists in the array:

*[_type == "post" && "category-id-here" in categories[]._ref]

This checks if the category ID appears in the _ref field of any item in the categories array.

Filter by Category Slug

If you want to filter by a category's slug (after dereferencing):

*[_type == "post" && "my-category-slug" in categories[]->slug.current]

The -> operator dereferences each category reference and lets you access its properties.

Filter by Multiple Categories (OR)

To find posts that have any of several categories:

*[_type == "post" && count((categories[]._ref)[@ in ["cat-id-1", "cat-id-2"]]) > 0]

Or with slugs:

*[_type == "post" && count((categories[]->slug.current)[@ in ["action", "thriller"]]) > 0]

Filter by Multiple Categories (AND)

To find posts that have all of the specified categories:

*[_type == "post" && count((categories[]->slug.current)[@ in ["action", "thriller"]]) == 2]

Just adjust the == 2 to match the number of categories you're requiring.

Using the references() Function

For a cleaner syntax when checking a single category:

*[_type == "post" && references("category-id-here")]

This checks if the document references the specified ID anywhere in its reference fields.

The key thing to remember is that categories[]._ref gives you an array of the referenced IDs, and you can use the in operator or count() function to check membership. The -> operator is super handy for accessing properties of the referenced documents directly in your query.

Show original thread
6 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?