Joint session with Vercel: How to build intelligent storefronts (May 15th)

Best approach for retrieving translations in GROQ queries from frontend?

8 repliesLast updated: Dec 2, 2025

Hey, is there a best approach for dealing with retrieving translations when running groq queries from the frontend?
Some context to this question
👇• We are using the
intl plugin for sanity to handle localization for our app. • We had to do some digging in order to retrieve blog articles data + associated translations. In our search,
this article has been sort of a guiding beacon.
WHAT WE'VE ATTEMPTED:• WHAT WORKS WELL
👍 The groq query highlighted in the above article works well when we use it in our sanity studio. The query's reference to
__i18n_lang && __i18n_refs
is represented by
_lang && _langRefs
for us as we've configured it so. So the query ran on studio looks something like this:
*[_type == "blog"]{
  _id,
  _lang,
  _langRefs,
  !(_id match "i18n*") => {
  "translations": *[_id in path("i18n." + ^._id + ".*")]
  },
  _id match "i18n*" => {
  "translations":
    *[^._id in _langRefs[].ref._ref]{_id, _lang} + 
    *[^._id in _langRefs[].ref._ref][0]{
      "matches": *[_id in path("i18n." + ^._id + ".*")]{_id, _lang}
    }.matches
  },

In our Sanity Studio, the results from this query return a blog article + an array of its
translations
filled with data of the translated blog articles.
• WHAT DOESN'T WORK SO WELL
👎 When attempting the same query in our frontend Next.js environment, the results return a blog article however this time with an empty array of
translations
----

I feel like there is something we've underthought or overlooked in our approach here. I'm happy for any guidance that will help me get on the right path. Thank you
🙏
AI Update

I can see you're hitting a frustrating issue where your translation query works in Studio but returns empty translations on the frontend. This is a common problem when working with document-level localization patterns, and the root cause is almost certainly related to API perspectives.

The Core Issue: Different Perspectives Between Studio and Frontend

Your Sanity Studio's Vision tool and your frontend Next.js app are likely querying different "views" of your content. The API version 2025-02-19 introduced a breaking change where the default perspective switched from raw to published. This means:

Since your path("i18n." + ^._id + ".*") pattern is looking for documents with specific ID patterns, if those translation documents aren't published (they're only drafts), they won't appear in your frontend results.

Quick Fix: Set Your Perspective Explicitly

Try explicitly setting the perspective in your frontend Sanity client:

import { createClient } from '@sanity/client'

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'your-dataset',
  apiVersion: '2024-01-01', // or your current version
  perspective: 'previewDrafts', // Shows published + drafts, prioritizing drafts
  useCdn: false, // Disable CDN while debugging
})

The previewDrafts perspective will give you both published and draft documents, prioritizing drafts when both exist. You could also try perspective: 'raw' which shows all documents.

Verify Translation Document Publication Status

Your translation documents (those with i18n.* IDs) might exist only as drafts. To debug this, run a simple query from your frontend:

const result = await client.fetch('*[_id match "i18n.*"]')
console.log(result)

If this returns empty but works in Studio, you've confirmed it's a perspective/publication issue. Make sure your translation documents are actually published (not just saved as drafts). In Sanity, draft documents have a drafts. prefix in their _id, and they won't appear in queries using the published perspective.

Systematic Debugging Steps

About Your Query Pattern

The approach you're using with path("i18n." + ^._id + ".*") relies on document ID pattern matching, which is sensitive to perspective and publication state. The pattern itself is solid, but it requires that the documents it's looking for are visible in the perspective you're querying with.

For immediate debugging:

// In your frontend client configuration
const client = createClient({
  // ... your existing config
  perspective: 'previewDrafts',
  useCdn: false,
})

For production: Once you've confirmed the issue, decide on your approach:

The key insight here is that Studio and your frontend are querying different views of your content due to perspective differences. Studio's Vision tool often shows all documents (similar to raw perspective), while your frontend client—especially if using a recent API version—defaults to only published documents. This explains why you see translations in Studio but not in your Next.js app.

Show original thread
8 replies

Was this answer helpful?

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.

Related contributions