
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeYour validator is slow because it fetches all slugs from your entire dataset on every keystroke. The solution is to cache the slug list and reuse it across validation calls.
Here are a few strategies to speed this up:
The simplest fix is to fetch slugs once when the Studio loads and reuse that cached list:
import client from "part:@sanity/base/client";
// Cache slugs at module level
let slugsCache = null;
let cachePromise = null;
const getAllSlugs = () => {
// If we already have slugs, return them
if (slugsCache) return Promise.resolve(slugsCache);
// If a fetch is already in progress, return that promise
if (cachePromise) return cachePromise;
// Otherwise, start a new fetch
cachePromise = client
.withConfig({ apiVersion: "2021-10-21" })
.fetch(`*[defined(slug.current) && !(_id in path("drafts.**"))].slug.current`)
.then((slugs) => {
slugsCache = slugs;
cachePromise = null;
return slugs;
});
return cachePromise;
};
export const validateLink = (url) => {
if (url.startsWith("/")) {
const clean = url.split("#")[0].split("?")[0];
return getAllSlugs().then((allSlugs) => {
return allSlugs.includes(clean)
? true
: "Does not match any published slugs";
});
}
// ... test against http url patterns ...
};This caches the result in memory, so subsequent validations are instant. The cache persists until the Studio reloads.
For a more robust solution, listen to document changes and update your cache:
import client from "part:@sanity/base/client";
let slugsCache = new Set();
// Initial fetch
client
.withConfig({ apiVersion: "2021-10-21" })
.fetch(`*[defined(slug.current) && !(_id in path("drafts.**"))]{_id, "slug": slug.current}`)
.then((docs) => {
slugsCache = new Set(docs.map(d => d.slug));
});
// Listen for changes
client
.listen('*[defined(slug.current)]')
.subscribe((update) => {
if (update.type === 'mutation') {
// Refetch or update cache intelligently
// For simplicity, you could just refetch all slugs
}
});
export const validateLink = (url) => {
if (url.startsWith("/")) {
const clean = url.split("#")[0].split("?")[0];
return slugsCache.has(clean) ? true : "Does not match any published slugs";
}
};Consider whether you actually need URL validation, or if a reference field would work better. References give you a document picker and ensure links always point to real documents:
{
name: 'internalLink',
type: 'reference',
to: [{type: 'page'}, {type: 'post'}] // whatever doc types have slugs
}Then resolve the slug when querying. This is type-safe and doesn't require validation at all.
Every time someone types in the URL field, your validator:
With caching, you do steps 1-3 once, and step 4 becomes instant.
The 5000ms warning appears because Sanity expects validators to respond quickly (they run on every keystroke). Your approach works correctly, it's just the performance that needs optimization.
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