Sanity

GROQ vs GraphQL: Which Query Language Should You Use with Sanity?

Choosing between GROQ and GraphQL for your Sanity + Next.js project? This guide breaks down syntax, performance, and ecosystem trade-offs to help you pick the right tool.

June 26, 202612 min readMuhammad Zohaib Ramzan
GROQ vs GraphQL query language comparison for Sanity

Why Your Query Language Choice Matters

When building a content-driven application with Sanity and Next.js, one of the earliest architectural decisions you’ll face is how to query your data. Sanity supports two distinct query languages: its own GROQ (Graph-Relational Object Queries) and the widely adopted GraphQL. Both can retrieve the same data, but they differ significantly in syntax, philosophy, performance characteristics, and ecosystem fit.

Making the right choice early saves you from painful refactors later. This guide walks through both options in depth — with real syntax examples — so you can make a confident, informed decision for your project.

What is GROQ?

GROQ is Sanity’s native query language, designed from the ground up to query document-oriented content stores. It draws inspiration from functional programming and XPath, offering a concise, expressive syntax that feels natural once you get past the initial learning curve.

GROQ Strengths

  • Native Sanity support — no configuration required; works out of the box
  • Server-side filtering — filtering and projections happen on Sanity’s CDN, reducing payload size
  • Flexible projections — you define exactly which fields to return, inline, without a separate schema layer
  • Joins and dereferencing — follow references with the -> operator in a single query
  • Real-time subscriptions — GROQ powers Sanity’s live query listeners natively

GROQ Syntax Overview

A GROQ query starts with a filter expression inside *[...], followed by an optional projection in {...}:

*[_type == "post"]{ title, slug, publishedAt }

The * selects all documents, the filter narrows the set, and the projection shapes the output. It reads almost like a sentence once you’re familiar with it.

What is GraphQL?

GraphQL is a query language and runtime developed by Facebook (Meta) in 2012 and open-sourced in 2015. It has since become a dominant API standard across the industry, with a rich ecosystem of tooling, client libraries, and community support.

Sanity offers an optional GraphQL API that you can deploy from the CLI. Once deployed, it generates a typed schema from your Sanity schema definitions, giving you a fully functional GraphQL endpoint.

GraphQL Strengths

  • Strongly typed schema — auto-generated types enable powerful IDE tooling and compile-time safety
  • Broad ecosystem — Apollo Client, urql, React Query integrations, codegen, and more
  • Familiar to many teams — widely adopted; many frontend developers already know it
  • Introspection — clients can query the schema itself, enabling powerful developer tooling
  • Interoperability — works well in multi-source data federation scenarios

GraphQL Syntax Overview

A GraphQL query uses named operations and typed fields:

query GetPosts { allPost { title slug { current } publishedAt } }

The schema enforces the shape of every query, and the type system catches errors before runtime.

Side-by-Side Syntax Comparison

Let’s compare the two languages across four common operations.

Fetching a List of Posts

GROQ:

*[_type == "post"] | order(publishedAt desc) { _id, title, slug, publishedAt }

GraphQL:

query { allPost(sort: [{ publishedAt: DESC }]) { _id title slug { current } publishedAt } }

GROQ’s pipe operator | chains transformations cleanly. GraphQL’s sort arguments are more verbose but benefit from type-checking.

Filtering Documents

GROQ:

*[_type == "post" && category->slug.current == "technology" && !(_id in path("drafts.**"))]{ title, excerpt }

GraphQL:

query { allPost(where: { category: { slug: { current: { eq: "technology" } } } }) { title excerpt } }

GROQ’s filter syntax is more compact and supports complex boolean logic inline. GraphQL’s where input types are more structured but can become deeply nested.

Projections and Fragments

GROQ uses inline projections to reshape data:

*[_type == "post"]{ title, "authorName": author->name, "categoryTitle": category->title }

The "authorName": author->name syntax dereferences the author reference and renames the field in one step.

GraphQL uses fragments for reusable field sets:

fragment PostFields on Post { title author { name } category { title } } query { allPost { ...PostFields } }

Fragments promote reuse across queries, which is valuable in large codebases.

Nested References

GROQ follows references with -> and can go multiple levels deep:

*[_type == "post"]{ title, author->{ name, "bio": bio[0].children[0].text, image{ asset->{ url } } } }

GraphQL traverses nested types naturally:

query { allPost { title author { name bio image { asset { url } } } } }

GraphQL’s nested traversal feels more natural to developers coming from REST + JSON backgrounds. GROQ’s -> operator is more explicit about when a reference boundary is being crossed.

Performance Considerations

Performance is one of the most important practical differences between the two approaches.

GROQ Performance

GROQ queries are executed entirely on Sanity’s backend infrastructure, including the CDN edge layer. This means:

  • Filtering is server-side — only matching documents are returned
  • Projections reduce payload — you receive exactly the fields you request
  • CDN caching — GROQ queries are cached at the edge, giving you fast global response times
  • No over-fetching — the query result is precisely shaped to your needs

For most Sanity + Next.js projects, GROQ queries via the CDN are extremely fast and require no additional infrastructure.

GraphQL Performance

Sanity’s GraphQL API is also served via the CDN, but there are some nuances:

  • Schema deployment required — you must run sanity graphql deploy and redeploy when your schema changes
  • Resolver overhead — the GraphQL layer adds a thin resolution step on top of GROQ under the hood
  • N+1 risk — without careful query design, nested resolvers can multiply requests (though Sanity’s implementation mitigates this)
  • Caching complexity — Apollo Client’s normalized cache is powerful but adds client-side complexity

For most use cases the performance difference is negligible, but GROQ has a slight edge in raw simplicity and CDN efficiency.

Flexibility and Ecosystem

GROQ: Sanity-Native Power

GROQ is purpose-built for Sanity’s document model. It handles Portable Text, image asset references, and cross-document joins with minimal ceremony. When your schema evolves, GROQ queries adapt without redeployment — there’s no schema to regenerate.

The @sanity/client library and the next-sanity package both have first-class GROQ support, and Sanity’s Visual Editing and Content Lake features are all GROQ-native.

GraphQL: Broad Tooling

GraphQL’s ecosystem is vast. If your team already uses Apollo Client, you get a normalized client-side cache, React hooks, and a mature devtools experience. GraphQL Code Generator can produce fully typed TypeScript interfaces from your schema, which is a significant DX win on large teams.

GraphQL also shines in schema stitching and federation scenarios — if you’re combining Sanity data with other GraphQL APIs (e.g., a commerce platform or a custom backend), GraphQL provides a unified graph.

When to Choose GROQ

GROQ is the right choice when:

  • You are building a Sanity-first project and don’t need to federate with other GraphQL APIs
  • You want zero configuration — no schema deployment, no codegen pipeline
  • Your team is small or you’re moving fast and want to iterate on queries without redeployment
  • You need real-time listeners or Sanity’s Live Content API
  • You’re using Sanity Visual Editing or Presentation tool, which are GROQ-native
  • You want the most direct, efficient path from Sanity’s Content Lake to your Next.js components

When to Choose GraphQL

GraphQL is the right choice when:

  • Your team has strong existing GraphQL expertise and tooling (Apollo, codegen, etc.)
  • You need end-to-end TypeScript types generated from your schema automatically
  • You are federating multiple data sources into a single GraphQL graph
  • Your project is large enough that fragment reuse and schema introspection provide meaningful DX benefits
  • You have a mobile app or third-party client that already consumes a GraphQL API

Using Both Together

It’s worth noting that GROQ and GraphQL are not mutually exclusive. Some teams use GROQ for server-side data fetching in Next.js (where performance and simplicity matter most) while exposing a GraphQL API for mobile clients or third-party integrations.

Sanity’s architecture supports this hybrid approach cleanly — you can deploy the GraphQL API alongside your GROQ-powered Next.js app without conflict. Just be mindful of keeping your schema deployment in sync when you take this route.

Conclusion

For the majority of Sanity + Next.js projects, GROQ is the recommended starting point. It requires no configuration, delivers excellent performance via Sanity’s CDN, and integrates seamlessly with every Sanity feature including Visual Editing, live queries, and Portable Text.

Reach for GraphQL when your team’s existing tooling, TypeScript codegen requirements, or multi-source federation needs make it the better fit. Both are well-supported, and you can always introduce GraphQL later without abandoning your GROQ queries.

The best query language is the one that makes your team productive and your application fast — and for most Sanity projects, that’s GROQ.