
Using Sanity Media Library Plugins
Sanity media library plugins extend Sanity Studio's basic asset picker with proper asset management. The most widely used is sanity-plugin-media, which adds a Media tool to Studio for browsing, searching, tagging and organising images and files into folders, and also replaces the picker on image and file fields. Asset source plugins, such as sanity-plugin-asset-source-unsplash and sanity-plugin-cloudinary, let editors pull assets from external services. For organisation-wide asset management across several projects, Sanity also offers Media Library, a separate app available as an add-on for certain Enterprise plans, which you connect to Studio with mediaLibrary: { enabled: true } in sanity.config.ts.
Out of the box, Studio lets editors upload a file or pick one they've uploaded before, and that's about it. There's no search by tag, no folders and no way to see where an asset is used or delete unused ones in bulk. That's fine for a small blog and painful for a team with thousands of images. This guide explains what each option does, how to install and configure it, how the data is stored and queried, and how to choose between them.
What the Default Asset Picker Does
Every image and file field in Studio has an upload button and a browse option. Browsing shows a grid of previously uploaded assets of the right type, which you can select to reuse. That covers the basics, but it lacks:
- Organisation: No tags, folders or collections.
- Search: Limited ways to find a specific asset among thousands.
- Asset-level metadata editing: No interface for the
title,descriptionandaltTextfields that exist on asset documents. - Bulk actions: No multi-select delete or tagging.
Each tool below fills some or all of these gaps. If you haven't worked with asset documents yet, managing files and assets in Sanity explains how they're stored.
Your Options at a Glance
| Tool | What it is | Where assets live | Plans |
|---|---|---|---|
| Default asset source | Built-in upload and browse | Your dataset | All |
sanity-plugin-media | Studio tool and asset source with tags and folders | Your dataset | All (open source plugin) |
| Asset source plugins | Pickers for Unsplash, Cloudinary and other services | Copied into your dataset, or referenced externally | All, plus the service's own terms |
| Media Library | Organisation-wide asset management app | A shared library for your organisation | Add-on for certain Enterprise plans |
You can combine them. Many teams use the Media plugin for day-to-day management and add Unsplash for stock photos.
The Media Plugin
sanity-plugin-media is an open source plugin that now lives in Sanity's official plugins repository. It adds a Media tool to your Studio navigation and registers itself as an asset source on image and file fields.
Install and Configure
npm install sanity-plugin-media
// sanity.config.ts
import { defineConfig } from "sanity";
import { structureTool } from "sanity/structure";
import { visionTool } from "@sanity/vision";
import { media } from "sanity-plugin-media";
import { schemaTypes } from "./schemaTypes";
export default defineConfig({
name: "default",
title: "My Studio",
projectId: "abc123",
dataset: "production",
plugins: [structureTool(), media(), visionTool()],
schema: { types: schemaTypes },
});
That's all you need. Editors now see a Media tool in the top navigation, and the browse menu on image and file fields gains a Media option.
What You Get
According to the plugin's README, its main features are:
- Batch uploads with drag and drop.
- Editing asset fields such as
title,description,altTextandoriginalFilenamedirectly on the asset. - Tags that you can create, rename and assign individually or in bulk.
- Folders in a sidebar, with drag and drop to move assets between them.
- Faceted search by tag, folder, usage, file size, orientation, type and more, plus text search across title, description and alt text.
- Bulk selection and deletion, with shift-click and cmd or ctrl-click.
- Previews for audio and video, and a subset of EXIF data when it's been extracted.
- Virtualised grid and table views that stay fast with thousands of assets, plus real-time updates from other editors.
Plugin Options
The plugin accepts a configuration object. These are the options you're most likely to use:
media({
creditLine: {
enabled: true,
excludeSources: ["unsplash"],
},
maximumUploadSize: 10_000_000, // bytes
directUploads: true,
excludeTags: ["archived"],
});
creditLine: Adds a credit line field for photographer or licence information.excludeSourcesstops editors overwriting credits that come from services like Unsplash.maximumUploadSize: Caps uploads through the plugin, in bytes. Useful for stopping 50 MB camera files.directUploads: Set tofalseto stop uploads through the plugin interface.excludeTags: Hides assets with these tag slugs from the browser and picker.locales: An array of{ id, title }objects that makes title, alt text, description and credit line localisable, with a tab per language.
Where the Data Is Stored
This is the most important thing to understand about the plugin. It writes directly to asset documents (sanity.imageAsset and sanity.fileAsset), not to the documents that use them:
- Text fields like
altText,titleanddescriptionare part of Sanity's asset schema, so they're stored as top-level properties on the asset. - Tags are
media.tagdocuments, stored as weak references inopt.media.tagson each asset. - Folders are
media.folderdocuments, with the asset's folder stored as a weak reference atopt.media.folder.
Because the values live on the asset, they're global. Set the alt text once and every document that uses the image can read it. That's ideal for values that describe the image itself, but not for context-specific ones. A caption that changes per article still belongs in the image field's own fields.
Query Plugin Data
Dereference the asset to read plugin fields:
*[_type == "post" && slug.current == $slug][0]{
title,
mainImage{
alt,
asset->{
url,
altText,
title,
description,
"tags": opt.media.tags[]->name.current,
"folder": opt.media.folder->name
}
}
}
A common pattern is to prefer the document-level alt text and fall back to the asset's:
"alt": coalesce(mainImage.alt, mainImage.asset->altText)
That gives editors a sensible default while still letting them override it per use. The full approach to alt text is covered in alt text requirements for Sanity images.
The README notes that opt.media tags and folders aren't available through Sanity's GraphQL API, because opt isn't part of Sanity's asset schema. Use GROQ if you need them.
Auto-Tagging Fields
You can pre-filter the browser and tag assets automatically when an editor selects them in a specific field. The mediaField helper wires this up:
// schemaTypes/product.ts
import { defineType, defineField } from "sanity";
import { mediaField } from "sanity-plugin-media";
export const product = defineType({
name: "product",
type: "document",
fields: [
defineField({ name: "name", type: "string" }),
mediaField({
name: "image",
type: "image",
mediaTags: ["product"],
}),
],
});
Editors choosing a product image see product-tagged assets first, and any asset they pick gets the product tag. Tags that don't exist yet are created automatically unless you set createTagsOnUpload: false.
Hide the Tag Type From Structure
The plugin registers a media.tag document type. If you use the default structure, it appears in your content list. To hide it, define a custom structure with Structure Builder that lists your own document types and leaves media.tag out.
Asset Source Plugins
Asset source plugins add new options to the browse menu on image and file fields. When there's more than one source, the select button becomes a drop-down.
Unsplash
npm install sanity-plugin-asset-source-unsplash
// sanity.config.ts
import { unsplashImageAsset } from "sanity-plugin-asset-source-unsplash";
export default defineConfig({
// ...
plugins: [structureTool(), media(), unsplashImageAsset()],
});
Editors can search Unsplash from inside Studio. Selected photos are copied into your dataset as normal image assets, with source information on the asset document so you can trace them back. Check Unsplash's licence and attribution guidelines for your use.
Cloudinary
sanity-plugin-cloudinary provides three plugins for different setups:
cloudinaryAssetSourcePlugin: Adds Cloudinary as an asset source. Images are copied into Sanity and served from Sanity's CDN.cloudinarySchemaPlugin: Adds acloudinary.assetfield type. Assets stay in Cloudinary and are served from Cloudinary's CDN.cloudinaryReferencePlugin: Stores Cloudinary assets as reusable document references.
import { cloudinaryAssetSourcePlugin } from "sanity-plugin-cloudinary";
export default defineConfig({
// ...
plugins: [structureTool(), cloudinaryAssetSourcePlugin()],
});
Choose the asset source mode if you want everything in Sanity, and the schema mode if your organisation already relies on Cloudinary's transformations and delivery.
Other Sources
Sanity's official plugins repository also includes integrations for Aprimo, Bynder, Shopify assets and Mux video, among others. The Sanity Exchange lists community plugins too. Video is a good example of where an external service makes sense, because Content Lake stores video as a plain file with no streaming.
Control Which Sources Appear
You can control sources globally in sanity.config.ts through form.image.assetSources and form.file.assetSources, or per field with options.sources.
The global options accept either an array or a function, and they behave differently. Sanity's docs explain that a static array appends to the existing sources, while a function replaces them with whatever it returns:
// sanity.config.ts
import { mediaAssetSource } from "sanity-plugin-media";
import { unsplashAssetSource } from "sanity-plugin-asset-source-unsplash";
export default defineConfig({
// ...
form: {
image: {
// Function: replaces the list. Remove Unsplash from the 'logo' image type only.
assetSources: (previous, { schema }) =>
schema.name === "logo"
? previous.filter((source) => source !== unsplashAssetSource)
: previous,
},
file: {
// Don't offer the Media plugin for file fields
assetSources: (previous) =>
previous.filter((source) => source !== mediaAssetSource),
},
},
});
Per field, options.sources limits that field to specific sources, and an empty array removes the browse option entirely so the field is upload-only:
defineField({
name: "heroImage",
type: "image",
options: { sources: [mediaAssetSource] },
});
If no plugin fits, you can write your own asset source: a React component that calls onSelect with a URL, file, base64 string or existing asset ID. Sanity's custom asset sources documentation walks through the API.
Sanity Media Library
Media Library is Sanity's own digital asset management app, distinct from the plugin. It's a paid add-on for certain Enterprise plans, so check with Sanity sales before planning around it. The Sanity pricing guide gives an overview of the plans.
According to Sanity's documentation, Media Library lets you:
- Store assets centrally for use across multiple projects and datasets in your organisation.
- Describe assets with aspects, which are custom, schema-style metadata fields such as licence details or product references.
- Organise assets into nested folders and collections.
- Set assets to public or private, with signed, time-limited URLs for private assets.
- Manage versions of an asset and update the documents that use it.
- Handle video, with playback streamed through a dedicated video CDN.
It runs inside Sanity's Dashboard rather than in Studio itself.
Connect It to Studio
Media Library needs Studio v3.82.0 or later. Once it's enabled for your organisation, turn it on in your config. Sanity recommends token-based authentication, because some features don't work with cookie-based login:
// sanity.config.ts
import { defineConfig } from "sanity";
import { structureTool } from "sanity/structure";
export default defineConfig({
projectId: "abc123",
dataset: "production",
mediaLibrary: {
enabled: true,
// Optional: pin to a specific library
// libraryId: 'YOUR_MEDIA_LIBRARY_ID',
},
auth: {
loginMethod: "token",
},
plugins: [structureTool()],
schema: { types: [] },
});
To move editors entirely to Media Library, remove the default source, which is named sanity-default:
form: {
image: {
assetSources: (sources) => sources.filter((s) => s.name !== 'sanity-default'),
},
file: {
assetSources: (sources) => sources.filter((s) => s.name !== 'sanity-default'),
},
},
From Studio v4.2.0, you can also add selectable filters to an image field's Media Library picker using GROQ against the library's dataset:
defineField({
name: "heroImage",
type: "image",
options: {
mediaLibrary: {
filters: [
{
name: "Wider than 2000px",
query: "currentVersion->metadata.dimensions.width > 2000",
},
],
},
},
});
How Rendering Works
Linking a Media Library asset to a document creates a normal asset document in your dataset, carrying a media reference back to the library. You keep rendering images exactly as before, with @sanity/image-url and the project's CDN. Sanity's docs say all requests for Media Library assets should go through your project datasets, and that bandwidth for those requests counts towards the project. See using Sanity's image URL builder for the rendering side.
If you're moving from the Media plugin, Sanity publishes a migration guide covering moving assets, re-linking them and updating references.
Which Should You Choose?
- Small site, few editors: The default asset source is enough. Add
sanity-plugin-mediawhen finding images starts to take time. - Growing content team on one project:
sanity-plugin-mediagives you tags, folders, search, global alt text and bulk clean-up at no extra cost. - Heavy use of stock photography: Add the Unsplash source alongside whichever manager you use.
- Existing DAM such as Cloudinary or Bynder: Use the matching plugin so assets stay in the system your organisation already trusts.
- Many projects sharing one asset library, private assets or licensing workflows: That's what Media Library is for, if your organisation is on an eligible Enterprise plan.
FAQ: Sanity Media Library Plugins
sanity-plugin-media is an open source Studio plugin that manages assets in a single dataset with tags, folders and search. Media Library is a separate Sanity app, sold as an add-on for certain Enterprise plans, that manages assets across an organisation with aspects, private assets and versions.
Yes. It's an MIT-licensed plugin maintained in Sanity's official plugins repository. Assets you manage with it still count towards your project's normal asset storage and bandwidth.
On the asset document itself, in the altText field of sanity.imageAsset. That makes it global for every use of the image. Query it with asset->altText, and use a field on the image object for alt text that should vary by document.
Yes. The Unsplash asset source copies the selected photo into your dataset as a normal image asset and records where it came from on the asset document, so you render it like any other Sanity image.
Use a function for form.image.assetSources or form.file.assetSources in sanity.config.ts that filters out the source named sanity-default. A function replaces the list of sources, while a static array would append to it.
Sanity's docs list Studio v3.82.0 or later for using Media Library assets in Studio, and v4.2.0 or later for Media Library filters on image fields. Your organisation must also have Media Library enabled.
Conclusion
Sanity's default asset picker is deliberately minimal, and the plugin ecosystem fills the gaps. sanity-plugin-media is the natural first step for most teams, adding tags, folders, search and global asset fields like alt text without changing how you render images. Asset source plugins bring in Unsplash, Cloudinary and other services, and you control where each one appears with assetSources and per-field sources.
For organisations that need one library across many projects, with private assets and structured metadata, Media Library is Sanity's answer, provided you're on an eligible Enterprise plan. Whichever you choose, assets still reach your front end through your project's dataset and CDN, so your rendering code stays the same as your asset management grows.


