How to get file URL from Sanity asset reference in Next.js?

11 replies
Last updated: Apr 29, 2022
Hello everyone, I'm trying to get a file URL to be able to download it from my Next.js Front-end, I have tried "FileURL": file.asset->url, keeps returning undefined, could anyone point me in the right direction?
AI Update

When working with file assets in Sanity, there are a couple of approaches to get the download URL in your Next.js frontend:

Option 1: Properly Resolve the Asset Reference in GROQ

The issue with file.asset->url returning undefined is that you need to fully resolve the asset reference. Try this GROQ query pattern:

*[_type == "yourDocType"] {
  ...,
  "fileUrl": file.asset->url
}

However, if that's still returning undefined, you might need to resolve it more explicitly:

*[_type == "yourDocType"] {
  ...,
  file {
    asset-> {
      url
    }
  }
}

Or spread the asset fields:

*[_type == "yourDocType"] {
  ...,
  file {
    ...,
    asset-> {
      _id,
      url,
      originalFilename,
      size
    }
  }
}

Instead of resolving the URL in GROQ, you can use the @sanity/asset-utils package to build URLs from the asset reference. This is often more efficient since you avoid extra resolving in your query:

Install the package:

npm install @sanity/asset-utils

Use a simpler GROQ query:

*[_type == "yourDocType"] {
  ...,
  file
}

Build the URL in your frontend:

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

// In your component
const fileUrl = buildFileUrl(file, {
  projectId: 'your-project-id',
  dataset: 'your-dataset'
})

You can also get additional file information like size and original filename:

*[_type == "yourDocType"] {
  ...,
  file {
    asset-> {
      _id,
      originalFilename,
      size
    }
  }
}

Then in your frontend:

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

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

const fileName = file.asset.originalFilename
const fileSize = file.asset.size

Enabling Downloads

To trigger a download (rather than opening in browser), append ?dl= to the URL:

<a href={`${fileUrl}?dl=`} download>
  Download File
</a>

The ?dl parameter is a Sanity CDN feature that forces the browser to download the file instead of displaying it.

Summary

The @sanity/asset-utils approach is generally preferred because:

  • It avoids unnecessary API calls to resolve references
  • It works with just the asset _ref or _id
  • It's more performant for your GROQ queries
  • You have full control over URL building in your frontend code
Show original thread
11 replies
Hi Crypticz. Can you post the query you’re trying currently and we can adjust it from there?
Thank you. And
file
is currently returning an
asset
property with a
_type
and
_ref
?
Okay, great. In Vision, what do you see when you run this?

*[_type == "portfolio"][0]{
    title,
    file,
  }
Yes. If you’re getting something like this:

"result": {
  "file": {
    "_type": "file",
    "asset": {
      "_ref": "file-81b6cdbf87a897608ee9816390a523725479cc32-pdf",
      "_type": "reference"
    }
  },
  "title": "Some title"
}
… then your original query should work, unless your slug param isn’t being applied correctly.
Okay, thanks for that. Looks like you want
"FileURL": file.file.asset->url
.
It’ll all live under
file.file.asset->
, so you can either get each property or use a projection like you did for
owninguser
.
The former:

'fileURL': file.file.asset->url,
'fileSize': file.file.asset->size,
// ...
The latter:


'file': file.file.asset->{
  url,
  size,
  // ...
}
Is this your new query?

const query = `*[_type == "portfolio" && slug.current == $pageSlug][0]{
    title,
    thumbnail,
    description,
    slug,
    "FileInfo": file.file.asset->{
      url,
      size,
      title
    },
    owninguser->{
      name,
      slug,
      image
    },
    images
  }`
And when you say “I changed it to this using
FileInfo.url
that no longer works,” where are you doing that and are you getting errors, no data, etc.?
Can you access the other properties coming back from your query? If so, have you asked for
FileInfo
? Sometimes that looks something like:

export default function Post({ post }) {
  const {
    title,
    FileInfo
  } = post;
Great. In that case, you could do:

<a href={portfolio?.FileInfo.url}></a>
Or you can do this first:


const { FileInfo } = portfolio;
and then use your original link code.

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?