๐Ÿ”ฎ Sanity Create is here. Writing is reinvented. Try now, no developer setup

Best approach for retrieving translations when running groq queries from the frontend

8 replies
Last updated: Oct 4, 2021
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.
๐Ÿ‘ 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 match "i18n*") => {
  "translations": *[_id in path("i18n." + ^._id + ".*")]
  _id match "i18n*" => {
    *[^._id in _langRefs[].ref._ref]{_id, _lang} + 
    *[^._id in _langRefs[].ref._ref][0]{
      "matches": *[_id in path("i18n." + ^._id + ".*")]{_id, _lang}

โœ… In our Sanity Studio, the results from this query return a blog article + an array of its
filled with data of the translated blog articles.
๐Ÿ‘Ž 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

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
Oct 4, 2021, 9:20 AM
Are you fetching data with a token? Because of the id structure of the translations they are treated as private: https://www.sanity.io/docs/ids#fdc25ada5db2
Oct 4, 2021, 1:52 PM
I would like to think so but maybe we are not ๐Ÿคท
So we initially configure our sanity client using
package. There is a
that takes into account all the configurations needed. In here is where we configure which dataset, project etc
When making the above mentionedgroq query, we're utilizing
method from

const query = groq`*[_type == "blog"]{
  !(_id match "i18n*") => {
  "translations": *[_id in path("i18n." + ^._id + ".*")]
  _id match "i18n*" => {
    *[^._id in _langRefs[].ref._ref]{_id, _lang} + 
    *[^._id in _langRefs[].ref._ref][0]{
      "matches": *[_id in path("i18n." + ^._id + ".*")]{_id, _lang}
The calling of this query in
is as follows:
  const [blogDataWithTranslations] = await getClient(preview).fetch(query);

--- > Do you mean there should be a token added to the fetch call?
Oct 4, 2021, 2:10 PM
Yes, because if you are fetching data without a token you not get the private documents. You can read more about that here: https://www.sanity.io/docs/keeping-your-data-safe
An alternative is to change the id structure of the translated docs, but that will make querying more difficult:
I had this exact problem the first time I used the plugin
Oct 4, 2021, 2:32 PM
Ooo wow ๐Ÿ˜ฎ
So just so I'm clear on this:
1. I would need to generate an API token for my dataset in my
sanity.io/manage environment. 2. Use the token in the
Oct 4, 2021, 2:37 PM
I'm assuming it passes as some sort of parameter in the fetcher-->
await getClient(preview).fetch(query, {ACCESS_TOKEN});
Oct 4, 2021, 2:37 PM
you include your token when creating clients via next-sanity, i.e

const client = createClient({
  projectId: ...,
  dataset: ...,
  token: 'token here'
I usually create a separate clients for client-side queries, without a token
Oct 4, 2021, 2:41 PM
That was it! Thank you both, this helped a lot ๐Ÿ™
Oct 4, 2021, 3:00 PM
Sorry for the late reply, glad to hear that you got good help from others in the meantime Malcolm! ๐Ÿ˜„ ๐ŸŽ‰
Oct 4, 2021, 5:21 PM

Sanityโ€“ build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?