Custom URL validator timing out when checking slugs against published documents
Your 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:
1. Cache the slug list outside the validator function
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.
2. Use a listener to keep the cache fresh
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";
}
};3. Use a reference field instead
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.
Why your current code is slow
Every time someone types in the URL field, your validator:
- Makes a network request to Sanity
- Fetches potentially thousands of documents
- Extracts all slugs
- Checks if the typed value matches
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.
Show original thread17 replies
Sanity – Build the way you think, not the way your CMS thinks
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.