
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeHey there! Great question about implementing a "radio button" style behavior for boolean fields across documents. You want only one document to have active: true at a time, with the others automatically set to false when a new one is selected.
The best approach is to create a custom input component for your boolean field that automatically updates other documents when toggled. Here's how to implement it:
Create a custom input component that uses the Sanity client to update other documents when the boolean is toggled:
// components/ExclusiveBooleanInput.tsx
import {BooleanInputProps, set, unset} from 'sanity'
import {Switch, Stack, Text} from '@sanity/ui'
import {useCallback} from 'react'
import {useClient} from 'sanity'
export function ExclusiveBooleanInput(props: BooleanInputProps) {
const {value, onChange} = props
const client = useClient({apiVersion: '2024-01-01'})
const handleChange = useCallback(
async (event: React.ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.checked
// If setting to true, deactivate all other documents
if (newValue) {
try {
// Find all other documents where active is true
const activeDocuments = await client.fetch(
`*[_type == "myschema" && active == true && _id != $currentId]._id`,
{currentId: props.id}
)
// Create a transaction to update all active documents
if (activeDocuments.length > 0) {
const transaction = client.transaction()
activeDocuments.forEach((docId: string) => {
transaction.patch(docId, {set: {active: false}})
})
await transaction.commit()
}
} catch (error) {
console.error('Error deactivating other documents:', error)
}
}
// Update the current document's value
onChange(newValue ? set(newValue) : unset())
},
[client, onChange, props.id]
)
return (
<Stack space={3}>
<Switch
checked={value || false}
onChange={handleChange}
/>
{value && (
<Text size={1} muted>
This is the active event. Enabling another will deactivate this one.
</Text>
)}
</Stack>
)
}Then register it in your schema:
// schemas/myschema.ts
import {ExclusiveBooleanInput} from '../components/ExclusiveBooleanInput'
export default {
name: 'myschema',
type: 'document',
title: 'My Schema',
description: 'MySchema',
fields: [
{
name: 'active',
title: 'Active',
type: 'boolean',
components: {
input: ExclusiveBooleanInput
}
},
{
name: 'name',
title: 'Event Name',
type: 'string'
}
]
}handleChange function is calledtrue, the component first queries for all other documents of type myschema where active is true (excluding the current document)active: falseonChange prop with a PatchEventThis approach follows the patterns described in the Custom Input Components documentation, where custom components receive props like value, onChange, and access to the document id through the props object.
Real-time updates: The Studio will show the changes in real-time thanks to Sanity's collaborative features, but editors may need to refresh to see the deactivation of other documents if they have them open
Permissions: Make sure the user has write permissions for all documents of this type, otherwise the transaction will fail
Draft vs Published: This implementation updates both drafts and published versions. If you want different behavior, you'll need to handle draft IDs (which have drafts. prefix) separately
Performance: If you have many documents, consider adding pagination or limiting the query scope
If you want a simpler approach that doesn't auto-update but prevents mistakes, you could add a custom validation rule that checks if another document is already active:
{
name: 'active',
title: 'Active',
type: 'boolean',
validation: Rule => Rule.custom(async (value, context) => {
if (!value) return true // If false, no validation needed
const {document, getClient} = context
const client = getClient({apiVersion: '2024-01-01'})
const otherActive = await client.fetch(
`count(*[_type == "myschema" && active == true && _id != $id])`,
{id: document._id}
)
return otherActive > 0
? 'Another event is already active. Please deactivate it first.'
: true
})
}The custom input component approach gives the best user experience since it handles everything automatically for your editors!
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