Skip to main content
GROQ (Graph-Oriented Query Language) is Sanity’s powerful query language for retrieving and transforming content from your Content Lake.

What is GROQ?

GROQ is designed for querying JSON documents with a syntax that:
  • Filters documents based on conditions
  • Projects specific fields from documents
  • Joins data from referenced documents
  • Transforms data with expressions and functions
  • Sorts and slices result sets

Using the client in Studio

The useClient hook provides a configured Sanity client:
import {useClient} from 'sanity'

function MyComponent() {
  const client = useClient({apiVersion: '2025-02-07'})
  
  // Use client to query
  const fetchAuthors = async () => {
    const authors = await client.fetch(
      `*[_type == "author"]`
    )
    return authors
  }
}
Always specify an apiVersion when using useClient to prevent breaking changes.

Basic GROQ syntax

1
Filter documents by type
2
Fetch all documents of a specific type:
3
*[_type == "author"]
4
This returns all author documents.
5
Project specific fields
6
Select only the fields you need:
7
*[_type == "author"] {
  name,
  role,
  "imageUrl": image.asset->url
}
8
Filter with conditions
9
Add conditional filters:
10
*[_type == "author" && role == "developer"]
11
Sort results
12
Order results by a field:
13
*[_type == "author"] | order(name asc)

Common query patterns

import {useClient} from 'sanity'

function AuthorProfile({authorId}: {authorId: string}) {
  const client = useClient({apiVersion: '2025-02-07'})
  
  const fetchAuthor = async () => {
    const author = await client.fetch(
      `*[_type == "author" && _id == $id][0] {
        name,
        role,
        image,
        "bookCount": count(*[_type == "book" && references(^._id)])
      }`,
      {id: authorId}
    )
    return author
  }
}

Reference following

GROQ makes it easy to follow references between documents:
*[_type == "book"] {
  title,
  author->{
    name,
    role,
    "bestFriend": bestFriend->name
  }
}
The -> operator dereferences a reference field.

Projections and transformations

Reshape your data in the query:
*[_type == "author"] {
  "authorName": name,
  "isDesigner": role == "designer",
  "fullProfile": {
    "name": name,
    "role": role,
    "hasImage": defined(image)
  }
}

Functions in GROQ

GROQ provides useful functions:
*[_type == "author"] {
  name,
  "uppercase": upper(name),
  "lowercase": lower(name),
  "length": length(name)
}
*[_type == "author"] {
  name,
  awards,
  "awardCount": count(awards),
  "hasAwards": count(awards) > 0
}
*[_type == "book"] {
  title,
  "status": select(
    publicationYear > 2023 => "new",
    publicationYear > 2020 => "recent",
    "classic"
  )
}

Parameters in queries

Use parameters for dynamic queries:
import {useClient} from 'sanity'

const client = useClient({apiVersion: '2025-02-07'})

const query = `*[_type == "book" && publicationYear >= $year] {
  title,
  publicationYear,
  author->name
}`

const recentBooks = await client.fetch(query, {
  year: 2020
})
Always use parameters instead of string interpolation to prevent GROQ injection vulnerabilities.

Ordering and slicing

Control result ordering and pagination:
*[_type == "author"] | order(name asc) [0..10] {
  name,
  role
}
This returns the first 10 authors sorted by name.

Counting and aggregation

{
  "totalAuthors": count(*[_type == "author"]),
  "developers": count(*[_type == "author" && role == "developer"]),
  "designers": count(*[_type == "author" && role == "designer"])
}

Using the references function

Find documents that reference another document:
*[_type == "book" && references($authorId)] {
  title,
  publicationYear
}

Coalesce for fallbacks

Provide fallback values:
*[_type == "author"] {
  name,
  "displayName": coalesce(nickname, name, "Unknown"),
  "imageUrl": coalesce(image.asset->url, "/default-avatar.png")
}

Portable Text queries

Query rich text content:
*[_type == "post"] {
  title,
  "excerpt": pt::text(body),
  "plainText": array::join(body[].children[].text, " ")
}

Using in React hooks

Combine with React hooks for data fetching:
import {useEffect, useState} from 'react'
import {useClient} from 'sanity'

function AuthorList() {
  const client = useClient({apiVersion: '2025-02-07'})
  const [authors, setAuthors] = useState([])
  
  useEffect(() => {
    const query = `*[_type == "author"] | order(name asc) {
      _id,
      name,
      role,
      image
    }`
    
    client.fetch(query).then(setAuthors)
  }, [client])
  
  return (
    <ul>
      {authors.map((author) => (
        <li key={author._id}>{author.name}</li>
      ))}
    </ul>
  )
}

Real-time listening

Listen to document changes:
import {useEffect, useState} from 'react'
import {useClient} from 'sanity'

function LiveAuthorCount() {
  const client = useClient({apiVersion: '2025-02-07'})
  const [count, setCount] = useState(0)
  
  useEffect(() => {
    const query = `count(*[_type == "author"])`
    
    const subscription = client.listen(query).subscribe((update) => {
      client.fetch(query).then(setCount)
    })
    
    return () => subscription.unsubscribe()
  }, [client])
  
  return <div>Authors: {count}</div>
}

Testing queries in Vision

Use the Vision plugin to test queries:
import {defineConfig} from 'sanity'
import {visionTool} from '@sanity/vision'

export default defineConfig({
  // ... project settings
  plugins: [
    visionTool(),
  ],
})
Vision provides a GROQ playground in your studio for testing queries interactively.

Common patterns

Performance tips

  • Project early: Only select fields you need
  • Use parameters: Leverage query caching
  • Limit results: Always paginate large result sets
  • Index carefully: Ensure frequently queried fields are indexed
  • Avoid deep nesting: Limit reference depth when possible
GROQ queries are cached by the API. Identical queries with the same parameters will be served from cache.

Next steps

Build docs developers (and LLMs) love