Implementing Content Governance in Sanity: Validation Rules vs. Structure Builder
Decide between using server-side validation rules to block invalid data and Structure Builder filters to organize the editor experience in Sanity Studio.
11 May 2026, 08:50 UTC

Decision: How to enforce content governance in Sanity Studio
When managing a Sanity project, you must balance data integrity with editor experience. You often face a choice between preventing invalid data from being saved and simply organizing how that data is presented to the user. This is handled by two distinct mechanisms:
- Document validation rules: JavaScript functions attached to schema fields that run during content creation and updates. They can provide real-time feedback and, in many cases, prevent a document from being published or saved if constraints are not met.
- Structure Builder filters: GROQ (Graph-Relational Object Queries) based configurations that define which documents appear in the Studio's desk interface. These manage visibility but do not restrict the underlying data.
Constraints and decision factors
Choosing between these two depends on whether your primary goal is governance (data integrity) or curation (UI organization).
- Enforcement: If a field must follow a strict business rule (e.g., a unique ID), validation rules are mandatory. Structure Builder cannot block an API write.
- Performance: Validation rules run frequently during the editing session. Complex asynchronous validation (using GROQ) can introduce latency in the UI. Structure Builder queries run primarily when navigating the desk.
- User Feedback: Validation rules provide immediate, inline error messages. Structure Builder simply removes items from a list, which can confuse editors if they expect a document to be there.
- Security: While Studio validation is visible to the user, critical constraints should be mirrored or enforced via server-side logic to prevent bypasses via the API.
Comparison of Governance Options
| Aspect | Document Validation Rules | Structure Builder Filters |
|---|---|---|
| Primary Goal | Data Integrity | Editor Experience |
| Mechanism | JS / Async Functions | GROQ Queries |
| Effect | Blocks/Warns on save | Hides/Shows in UI |
| Feedback | Inline error messages | Filtered list view |
| Execution | On field change/save | On desk navigation |
Trade-off Summary
Use Document Validation Rules when the cost of invalid data is high (e.g., broken URLs, missing required relational links). Use Structure Builder when the cost of a cluttered UI is high (e.g., hiding "Draft" documents from a "Published" folder).
Implementation: Asynchronous Unique-Slug Validation
A common requirement is ensuring a slug is unique across all documents of a specific type. Because this requires checking other documents in the dataset, it requires an asynchronous validation rule using a GROQ query.
// schemas/post.js
export default {
name: 'post',
title: 'Blog Post',
type: 'document',
fields: [
{
name: 'title',
title: 'Title',
type: 'string',
validation: Rule > Rule.required()
},
{
name: 'slug',
title: 'Slug',
type: 'slug',
options: { source: 'title' },
validation: Rule => Rule.custom(async (slug, context) => {
if (!slug?.current) return true;
const { getClient, documentId } = context;
const client = getClient({ apiVersion: '2023-05-03' });
// Query for any other document of type 'post' with the same slug
const duplicate = await client.fetch(
`*[_type == "post" && slug.current == $slug && _id != $id][0]`,
{ slug: slug.current, id: documentId }
);
return !duplicate || 'This slug is already in use by another post';
})
}
]
};
Deployment and Execution
This configuration is placed within your schema definitions. The validation logic is executed within the Sanity Studio environment. When an editor modifies the slug field, the custom rule triggers the getClient method to perform a real-time lookup against the Content Lake.
Verification Steps
- Trigger Conflict: Create a post with a slug (e.g.,
/hello-world). Create a second post with the same slug. The Studio should display the error: "This slug is already in use by another post". - Verify Identity: Edit the first post and save it without changing the slug. The validation should return
truebecause the_id != $idclause prevents the document from colliding with itself. - Network Audit: Open the browser DevTools Network tab. Observe the
queryrequests sent to the Sanity API as you type in the slug field to confirm the async validation is firing.
Limitations and Practical Checks
- API Latency: Every keystroke (or debounced change) in an async validation field triggers a network request. For extremely large datasets or high-frequency edits, this can impact perceived performance.
- Client-Side Bypass: Remember that Studio validation is a UI helper. If you have external scripts pushing data to Sanity via the API, these JS rules will not block those writes. You must implement server-side validation (e.g., via Sanity Webhooks or a middleware layer) for absolute security.
- Version Assumption: This implementation assumes Sanity Studio v3 and a compatible
apiVersion.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.