GROQ: Accessing file URLs within block content arrays

12 replies
Last updated: Jan 12, 2026
So I came across this groq query
// GROQ query
*[_type == 'movie'] {
  title,
  "manuscriptURL": manuscript.asset->url
}

// Then you can use the URL in HTML for example like this:
// <a href={`${manuscriptURL}?dl=`}>Manuscript</a>
for downloading files. However, what if the
file
is added to your block content? Is it as simple as doing
  "body": body[]{
    ...,
    _type == 'file' => {
      ...,
      "documentUrl": asset->url
    }
  }

AI Update

Yes, your approach is almost correct! When you have a file asset embedded in block content (Portable Text), you need to resolve the asset reference to get its URL. Your query structure is on the right track:

"body": body[]{
  ...,
  _type == 'file' => {
    ...,
    "documentUrl": asset->url
  }
}

However, there are a couple of important considerations based on the discussion from the Sanity community:

Performance optimization: If you need multiple fields from the asset (like size, original filename, etc.), avoid using multiple -> references for the same document. Instead, merge them into a single subquery:

"body": body[]{
  ...,
  _type == 'file' => {
    ...,
    ...(asset-> {
      "documentUrl": url,
      "documentSize": size,
      "documentOriginalFilename": originalFilename
    })
  }
}

Alternative approach using @sanity/asset-utils: Instead of resolving asset->url in your GROQ query, you can use the @sanity/asset-utils library to generate file URLs client-side. This approach is more efficient since you only need the asset reference ID:

"body": body[]{
  ...,
  _type == 'file' => {
    ...,
    // Just include the asset reference, resolve URL client-side
  }
}

Then in your code:

import { buildFileUrl } from '@sanity/asset-utils'

const fileUrl = buildFileUrl(fileBlock.asset, { 
  projectId: 'your-project-id', 
  dataset: 'your-dataset' 
})

Both approaches work, but using @sanity/asset-utils can be more performant since it avoids the extra dereferencing in GROQ. However, if you also need metadata like file size and original filename, you'll still need to resolve those from the asset in your query.

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