Sanity v3: Custom slug validation with hooks causing errors after migration

2 replies
Last updated: May 8, 2024
hi everyone, I need some help on
slug
I just migrating sanity studio to v3, and facing this issue

so I have an slug error regarding on custom validation
here's my custom slug validation code (it was fine on sanity studio v2)

import { useClient } from "sanity";

export function isPageSlugUnique(slug, options) {
  const client = useClient({ apiVersion: "2022-02-03" });
  const [result, setResult] = useState(false);

  const { document } = options;
  const id = document._id.replace(/^drafts\./, "");

  useEffect(() => {
    async function fetchDocument() {
      const params = {
        draft: `drafts.${id}`,
        published: id,
        site: document.site,
        slug,
      };

      const query = `!defined(*[!(_id in [$draft, $published]) && slug.current == $slug && site == $site][0]._id )`;
      const res = await client.fetch(query, params);

      setResult(res);
    }
    fetchDocument();
  }, []);

  return result;
}
and it always returning an error like this image
any advice to solve this?
AI Update

I can see you're running into the classic Studio v3 migration issue with custom slug validation! The problem is that in v3, you can't use React hooks like useClient in regular functions - hooks only work in React components.

Here's how to fix your custom slug validation for Studio v3:

The key change is that the isUnique function (and similar schema functions) receives a context object as the second parameter that contains the client. Here's the corrected approach:

export function isPageSlugUnique(slug, context) {
  const { document, getClient } = context;
  const client = getClient({ apiVersion: "2022-02-03" });
  
  const id = document._id.replace(/^drafts\./, "");
  const params = {
    draft: `drafts.${id}`,
    published: id,
    site: document.site,
    slug,
  };

  const query = `!defined(*[!(_id in [$draft, $published]) && slug.current == $slug && site == $site][0]._id)`;
  
  return client.fetch(query, params);
}

Then in your schema:

{
  name: 'slug',
  type: 'slug',
  options: {
    source: 'title',
    isUnique: isPageSlugUnique
  }
}

Key changes from v2 to v3:

  1. Remove React hooks - No more useClient(), useState(), or useEffect()
  2. Use context parameter - The second parameter contains getClient, document, and other utilities
  3. Return a promise directly - Just return the client.fetch() promise; Studio handles the async resolution
  4. Access document from context - Get document from the context parameter instead of options

This pattern is documented in this community thread where the Sanity team clarified that isUnique receives async (value, {client, document, getClient}) as parameters.

The reason your v2 code broke is that Studio v3 removed the parts system and hooks can only be used in React components, not in schema configuration functions. The context parameter is the new way to access Studio utilities in schema functions like isUnique, initialValue, and others!

Show original thread
2 replies
It’s number 2: breaking the rules of hooks. Your function isn’t a React component and so you can’t use the useEffect and useClient hooks. You’ll want to bring in the client in another way.
ah thats correct, it solved now 🎉

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.

Was this answer helpful?