How to enforce proper slug format validation in Sanity to prevent spaces and capitals?

7 replies
Last updated: Jan 27, 2026
I noticed today that
type: 'slug'
can have spaces and capitalizations in it. Someone on our team instead of clicking "Generate" on the slug typed something in and we didn't catch it before it was published which kinda messed things up. I can go back and correct it obviously but what is the best way to enforce a
type: 'slug'
to actually be a slug (at least the way I think of a slug) - do a custom validation against capital letters and spaces?
AI Update

You're absolutely right to be concerned about this! The slug field in Sanity doesn't actually enforce URL-friendly formatting by default when someone manually types into it - it only applies slugification when you click the "Generate" button. This is a common gotcha.

The best approach is to add custom validation to your slug field. Here's how you can enforce proper slug formatting:

{
  name: 'slug',
  type: 'slug',
  options: {
    source: 'title',
    maxLength: 96,
  },
  validation: Rule => Rule.required().custom((slug) => {
    if (!slug || !slug.current) {
      return 'Slug is required'
    }
    
    const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
    
    if (!slugRegex.test(slug.current)) {
      return 'Slug must be lowercase, contain only letters, numbers, and hyphens (no spaces or special characters)'
    }
    
    return true
  })
}

This regex pattern ensures the slug:

  • Contains only lowercase letters and numbers
  • Uses hyphens (not spaces) to separate words
  • Doesn't start or end with a hyphen
  • Has no special characters or uppercase letters

Additional options to consider:

  1. Make it read-only to force using the Generate button:
{
  name: 'slug',
  type: 'slug',
  options: {
    source: 'title',
  },
  readOnly: true, // Users can only use Generate button
}
  1. Custom slugify function to handle edge cases when the Generate button IS used:
{
  name: 'slug',
  type: 'slug',
  options: {
    source: 'title',
    slugify: input => input
      .toLowerCase()
      .replace(/\s+/g, '-')
      .replace(/[^\w-]+/g, '')
      .slice(0, 96)
  }
}

The validation approach is usually best because it catches the problem before publishing while still allowing manual editing when needed (like fixing typos). The readOnly approach is more restrictive but prevents the issue entirely if you never need manual slug editing.

Remember that the slug value is stored in slug.current, which is why the validation checks slug.current rather than just slug. You can also combine validation with a custom slugify function for comprehensive protection - the slugify handles the Generate button behavior, while validation catches any manual entries.

Show original thread
7 replies

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?