Module 03
Page 06
7 min

Image and File

Adding Image and File schema types, as well as installing the Media browser plugin


So far, we've mainly talked about storing data that can be stored in the form of Strings, Numbers, etc. But one of the cool things about Sanity is it's Asset Pipeline. We can treat both images and general files as schema types, and do a lot with them.

We won't delve into the power of the Asset Pipeline and Image Transformations here - we'll look more into that when we learn how to use References and GROQ. But before we start adding image and file schema types to our blog post document, let's modify our Sanity Studio.

Adding the Media browser

Out of the box, Sanity doesn't really have a good way to see prior images and files we have uploaded from a larger perspective. Luckily, a developer named Robin Pyon developed a plugin for Sanity to solve this problem.

Another great thing about Sanity is that there are tons of developers working on plugins, tools, and schemas to make your Sanity projects even better.

With Media browser, we'll be able to extract the metadata from our files and images, find where we used them, add tags, and more.

If your Sanity Studio is running in the terminal, press CONTROL+C to end your local development server.

npm install --save sanity-plugin-media

When completed, it look something like this:

146 packages are looking for fundingrun `npm fund` for details
found 0 vulnerabilities

Now, let's run npm run dev to get our local server up and running again. Next, we'll need to modify our sanity.config.js to add the plugin to our Studio.

Update the following:

...import {media} from 'sanity-plugin-media'
export default defineConfig({  ...,  plugins: [deskTool(), visionTool(), media()],
  schema: {    types: schemaTypes,  },})

Now, at the top of Studio, right next to Vision, you'll see a new Media tab. Click on it, and you'll see this:

We need to add some images to our blog posts, and then we'll be able to see more.

The Image schema type

Adding a schema type should be pretty familiar by now, but let's still go through it. We'll be adding a image for the cover of our blog post.

/schemas/blogPost.js
...,{  name: 'coverImage',  type: 'image',  title: 'Cover Image',  group: 'content',},...

Now in our blog post document, we'll see a new input field. There are a bunch of different ways to add images, and they're all hosted and stored by Sanity.

I used this blog post cover I made - which you can download below, but you can also use any image you want! Just upload it into the field and you're good to go.

I also added a cover image for the other blog post we made. Before we go see what changed in our Media browser - let's talk about what makes Image schema types so unique compared to just being files, or just any other traditional schema type.

Hotspots

When working with content, you sometimes need a single image to fulfill various different aspect ratios, sizes, etc. Sometimes, you have an image that might not conform to the aspect ratio you need.

We can enable the hotspot feature for an image schema field with the following:

/schemas/blogPost.js
{  name: 'coverImage',  type: 'image',  title: 'Cover Image',  group: 'content',  options: {    hotspot: true,  },},

Now, you'll see a new "crop" icon on the top right of the image. Click on it, and you'll see a box and circle you can manipulate to fill the various common aspect ratios at the bottom.

This is great if you want to display image content in various ways, whether its 16:9 in cards, panorama as the background of sections, or squares in a compact list.

Adding additional fields

What if we want to add a description to our image? Or alternative text? Or attribution? It doesn't make much sense for us to add an additional field to our blog post document - but we can add additional fields to the image schema type itself.

Let's add a description field to our image:

/schemas/blogPost.js
{  name: 'coverImage',  type: 'image',  title: 'Cover Image',  group: 'content',  options: {    hotspot: true,  },  fields: [    {      name: 'description',      type: 'string',      title: 'Description',    },  ],},

As you can see, we can add a new fields array to our image schema type. And within it, we added a string schema type. (You can add whatever schema types you want to this fields array, just like you can with a document. This will make sense soon.)

Now, if we see our blog post cover image field, things have changed, and we can add a description to our image:

As far as validation goes, we can add the good ole' required rule if you wanted to require all blog posts to have a cover image.

Checking back in with Media browser

Now, if we go back to Media browser, you'll see the images we added to our blog posts. If you click on one, you'll see related metadata, even which blog post we referenced. (This term, referenced, is important, and you'll want to remember this for the future, especially for images.)

Adding file schema types

File schema types are very similar to images, with the exception of not having the same level of metadata or transformation options.

You shouldn't use the file schema type for images - make sure you use Image if you want to take advantage of those major differences.

Let's add a file schema type to our blog post for an audio recording users can download and listen to.

...,{  name: 'audioFile',  type: 'file',  title: 'Audio File',  group: 'content',  options: {    accept: 'audio/*',  },},...

If you notice, we added an accept option set to audio/*. This is because we only want audio files - if we didn't have this, the input would accept any kind of file. If you try to upload a non-audio file, the field wouldn't accept it. This is helpful for specifying which types of files you want.

Just like images, files you upload will also show up in your Media browser with basic metadata and reference information.

Getting Image and File URLs via GROQ

There are a two ways to get the URLs of images and files via GROQ:

  1. Directly create the Image URL via the GROQ query
  2. Get the image schema via the GROQ query and create the URL using @sanity/image-url

Here, we'll go over the first way, as the second requires us to have a website to connect our Sanity data to, and we'll use Image Transformation and Optimization then.

Use the following query in Vision to get the URL of our cover image:

*[_type == "blogPost"][0] {  title,  "coverURL": coverImage.asset->url}

And you should get something like this as the result:

{…} 2 properties  coverURL:https://cdn.sanity.io/images/PROJECTID/production/43ba15a0ac5cdb288981a5e0d7a4430c4f546035-1920x1080.png  title:This is my first blog post! Can't you believe it??
The URL won't be the exact same, but you should see a URL.

Let's explain this line of GROQ: "coverURL": coverImage.asset->url

  • To get the URL this way, we need to attach the URL field we're obtaining to a field name, so we use coverURL. In this instance, since we're obtaining the URL indirectly, we need to give it a field name. (It can be anything, as long as we don't use it in the rest of the query, like title.)
  • All images and files contain an asset field, which references a ton of other data. To access this data, we use -> to the url field which exists in the image asset reference.
  • This pattern also applies to files - the implementation of Images and Files being references will make more sense soon.

If you don't need any of the image or file related metadata, this is a surefire way to obtain the URL - but you'll see later on why this isn't always the best way to do it.

Ritesh Kanchi © 2025