Adding a word counter to a content block in Sanity.io

3 replies
Last updated: Jul 9, 2020
Hey! I'm attempting to add a small word counter to a content block. I suppose this means getting the content and having a field to display the value. Is it best to create a custom field for this kind of thing?
AI Update

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.

This is the current setup for the content:
{
      name: 'content',
      title: 'Content',
      type: 'array',
      of: [
        {
          type: 'block',
          styles: [
            { title: 'Normal', value: 'normal' },
            { title: 'H2', value: 'h2' },
            { title: 'H3', value: 'h3' },
            { title: 'H4', value: 'h4' },
            { title: 'Quote', value: 'blockquote' },
          ],
          marks: {
            decorators: [
              { 'title': 'Strong', 'value': 'strong' },
              { 'title': 'Emphasis', 'value': 'em' },
              { 'title': 'Underline', 'value': 'underline' },
              { 'title': 'Strike', 'value': 'strike-through' }
            ]
          },
        },
      ],
    },
Check out this gist: https://gist.github.com/kmelve/5c8eb803382d44ddc5e6c91d28e99551
Used with the
inputComponent
property on the field config
Thanks! That looks very manageable

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?