Adding a word counter to a content block in Sanity.io
For displaying a word counter on a content block, you don't necessarily need to create a fully custom field type. The best approach depends on whether you want the counter to be read-only or interactive.
For a read-only word counter (most common use case), you can use the components property on your field schema to add a custom React component that calculates and displays the count. This is simpler than creating a full custom input component.
Here's a practical example:
import {Stack, Text} from '@sanity/ui'
const WordCounter = (props) => {
const {renderDefault, value} = props
// Calculate word count from your content
const wordCount = value ? value.split(/\s+/).filter(Boolean).length : 0
return (
<Stack space={3}>
{renderDefault(props)}
<Text size={1} muted>
Word count: {wordCount}
</Text>
</Stack>
)
}
// In your schema
{
name: 'content',
type: 'text', // or 'string'
components: {
input: WordCounter
}
}For Portable Text/Block Content, you'll need to extract text from the blocks array:
const countWordsInBlocks = (blocks) => {
if (!blocks) return 0
return blocks
.filter(block => block._type === 'block')
.map(block => block.children?.map(child => child.text).join(' ') || '')
.join(' ')
.split(/\s+/)
.filter(Boolean).length
}If you need more complex functionality (like setting word count limits or warnings), then creating a custom input component gives you full control over the rendering and behavior.
The components.input approach is generally preferred for simple enhancements like counters because it:
- Wraps the existing input (less code to maintain)
- Preserves all default Studio functionality
- Is easier to implement and update
You can also use Component Views to display document-level statistics in a separate tab, but that's more useful for overall document analytics rather than per-field counters.
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.