
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeWhat you're describing is an interesting constraint system where you want to ensure only 5 documents can exist at any time because there are only 5 category options available, and each document must use a unique category.
Here are a few approaches you could take:
You can create a custom validation rule that queries existing documents to check if a category is already in use:
{
name: 'category',
type: 'reference',
to: [{type: 'category'}],
validation: Rule => Rule.custom(async (value, context) => {
if (!value?._ref) return true;
const {getClient} = context;
const client = getClient({apiVersion: '2023-01-01'});
// Check if this category is already used by another document
const existingDocs = await client.fetch(
`*[_type == $docType && category._ref == $categoryRef && _id != $currentId]`,
{
docType: context.document._type,
categoryRef: value._ref,
currentId: context.document._id
}
);
if (existingDocs.length > 0) {
return 'This category is already in use';
}
return true;
}).required()
}You can filter out already-used categories from the reference field options using the filter option on reference fields:
{
name: 'category',
type: 'reference',
to: [{type: 'category'}],
options: {
filter: async ({document, getClient}) => {
const client = getClient({apiVersion: '2023-01-01'});
// Get all category IDs that are already used
const usedCategories = await client.fetch(
`*[_type == $docType && _id != $currentId].category._ref`,
{
docType: document._type,
currentId: document._id
}
);
return {
filter: '!(_id in $usedCategories)',
params: {usedCategories}
};
}
}
}You could also add validation at the document level to prevent publishing when all categories are taken:
{
name: 'myDocType',
type: 'document',
fields: [
// your fields including category
],
validation: Rule => Rule.custom(async (doc, context) => {
const {getClient} = context;
const client = getClient({apiVersion: '2023-01-01'});
const totalCategories = await client.fetch(`count(*[_type == "category"])`);
const usedCategories = await client.fetch(
`count(*[_type == $docType && _id != $currentId])`,
{
docType: doc._type,
currentId: doc._id
}
);
if (usedCategories >= totalCategories && !doc._id) {
return 'Cannot create new document: all categories are already in use';
}
return true;
})
}The combination of approach #2 (filtering available options) and #1 (validation) gives the best user experience - users only see available categories, and validation provides a safety net.
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.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store