Folders
Organize Media Library assets into a navigable hierarchy with folders.
Beta primitive
Folders are built on the hierarchy primitive in Content Lake, which is currently in public beta. The underlying API surface may change.
Concepts
A folder hierarchy in Media Library is made up of three document types from the Content Lake hierarchy primitive: sanity.tree, sanity.directory, and sanity.symlink.
Assets (sanity.asset) sit inside folders by carrying a parent reference to a sanity.directory.
Getting started with the API
These examples use @sanity/client configured against your Media Library. They also work via the standard Media Library mutate and query endpoints.
Step 1: Create the tree
Every folder hierarchy starts with one sanity.tree document.
import {createClient} from '@sanity/client'
const libraryId = '<your-library-id>'
const client = createClient({
apiVersion: '2025-02-19',
resource: {type: 'media-library', id: libraryId},
token: process.env.SANITY_TOKEN,
})
await client.createIfNotExists({
_id: `tree.${libraryId}`,
_type: 'sanity.tree',
name: 'folders',
})const libraryId = '<your-library-id>'
const token = '<personal-auth-token>'
await fetch(`https://api.sanity.io/v2025-02-19/media-libraries/${libraryId}/mutate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
mutations: [
{
createIfNotExists: {
_id: `tree.${libraryId}`,
_type: 'sanity.tree',
name: 'folders',
},
},
],
}),
})Media Library requires the tree's _id to be tree.{libraryId}.
Step 2: Create folders
Create a sanity.directory with a name and a parent reference. Top-level folders point at the tree; nested folders point at their parent directory.
// Use the same `tree.${libraryId}` ID created in Step 1
const treeId = `tree.${libraryId}`
// Top-level folder
const marketing = await client.create({
_type: 'sanity.directory',
name: 'Marketing',
parent: {_ref: treeId},
})
// Nested folder
const campaigns = await client.create({
_type: 'sanity.directory',
name: 'Campaigns',
parent: {_ref: marketing._id},
})For directory creation patterns, validation rules, and the error reference, see Creating directories in the hierarchy primitive.
Step 3: Place assets in a folder
For assets that are already uploaded, set parent on the sanity.asset document to move it into a folder.
await client
.patch(asset._id)
.set({parent: {_ref: folderId}})
.commit()Place assets inside a sanity.directory rather than directly under the tree. The schema permits both, but the Media Library app browses, lists, and operates on assets through directories.
Leave parent unset to keep an asset outside of the folder hierarchy.
Upload directly into a folder
When uploading a new asset, set the parent query parameter on the upload endpoint to place it in a folder in a single request, no follow-up patch needed.
parent must reference a sanity.directory.
sanity.tree is not a valid value, assets in Media Library cannot live directly under the tree, only inside a folder.
For the full upload pattern, see Upload assets programmatically.
Querying assets and folders
Media Library hierarchy is queried with standard GROQ. The same patterns documented in Querying the hierarchy apply
You can query assets inside a specific folder with:
*[_type == "sanity.asset" && parent._ref == $folderId]
Moving folders and assets
Patch parent to a new destination. The server validates the destination before any change is applied, see the error reference.
await client
.patch(folderToMove._id)
.set({parent: {_ref: newParentId}})
.commit()The same pattern applies to assets. Set parent to move an asset into a different folder, or unset it to remove the asset from any folder.
Shortcuts
A shortcut makes a single asset visible inside an additional folder without duplicating the underlying file. Shortcuts are sanity.symlink documents that reference a destination folder via parent and an asset via target.
await client.create({
_type: 'sanity.symlink',
parent: {_ref: destinationFolderId},
target: {_ref: assetId},
})Media Library constraint
In Media Library, target must reference a sanity.asset. Targeting a sanity.directory (a “folder shortcut”) is not supported and will not render correctly in the app.
Deleting folders
await client.delete(folderId)
No cascade delete
Deleting a folder does not cascade. The server rejects any delete where children still reference the folder via parent. See deleting a hierarchy primitive.
In the Media Library app, the delete folder action handles this for you. It deletes children recursively and displays a confirmation dialog that surfaces information about contents and potential warnings. See Deleting folders in the interface.