Adding a feature flag to a Sanity type and discussing possible solutions.
Great question! What you're looking for is a way to ensure only one document has isFeatured: true at a time, and automatically toggle off the previous one when a new one is marked as featured. This is definitely achievable in Sanity!
The best approach is to create a custom input component for your boolean field that checks for other featured items and automatically unsets them when the current one is toggled on. Here's how to implement it:
Custom Input Component Approach
Create a custom input component that uses the Sanity client to patch other documents:
import { useCallback } from 'react'
import { set, unset } from 'sanity'
import { Switch, Stack, Text } from '@sanity/ui'
import { useClient } from 'sanity'
export function FeaturedToggle(props) {
const { value, onChange } = props
const client = useClient({ apiVersion: '2024-01-01' })
const handleToggle = useCallback(async () => {
if (!value) {
// User is turning this one ON, so turn off all others
try {
// Find all other documents with isFeatured: true
const query = `*[_type == "myType" && isFeatured == true && _id != $currentId]`
const otherFeatured = await client.fetch(query, {
currentId: props.document._id
})
// Create a transaction to update all at once
let transaction = client.transaction()
// Unset isFeatured on all other documents
otherFeatured.forEach(doc => {
transaction = transaction.patch(doc._id, { unset: ['isFeatured'] })
})
// Execute the transaction
await transaction.commit()
// Now set this one to true
onChange(set(true))
} catch (error) {
console.error('Error updating featured status:', error)
}
} else {
// User is turning this one OFF
onChange(unset())
}
}, [value, onChange, client, props.document._id])
return (
<Stack space={2}>
<Switch
checked={value || false}
onChange={handleToggle}
/>
{value && (
<Text size={1} muted>
This is the featured item. Toggling another will unset this one.
</Text>
)}
</Stack>
)
}Then in your schema, use this custom component:
export default {
name: 'myType',
type: 'object',
title: 'My Type',
fields: [
// ... other fields
{
title: 'Is featured',
name: 'isFeatured',
type: 'boolean',
components: {
input: FeaturedToggle
}
},
],
initialValue: {
isFeatured: false,
},
}How It Works
- useClient hook - Gets a configured Sanity Client instance to perform API operations
- GROQ query - Finds all other documents of the same type with
isFeatured: true - Transaction - Uses the Mutation API's transaction support to atomically update multiple documents
- Patch operations - Unsets the flag on all other documents before setting it on the current one
- onChange with set/unset - In Studio v3, the
onChangecallback accepts patch operations directly using theset()andunset()functions imported fromsanity
The key advantage is that this gives immediate visual feedback to the user and handles the toggle behavior exactly as you described. The transaction ensures all patches happen atomically, maintaining data consistency.
Alternative: Sanity Functions (More Robust)
For a production-ready solution that works even when documents are updated via API or other means (not just through the Studio UI), consider using Sanity Functions to handle this logic server-side. You could create a function that listens to document mutations and automatically updates other documents when one is marked as featured, ensuring the logic runs regardless of how the content is edited.
The custom input component approach is perfect for your use case since it's user-friendly and provides the toggle behavior you're looking for! 🤗
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.