Sanity logosanity.ioAll Systems Operational© Sanity 2026
Change Site Theme
Sanity logo

Documentation

    • Overview
    • Platform introduction
    • Next.js quickstart
    • Nuxt.js quickstart
    • Astro quickstart
    • React Router quickstart
    • Studio quickstart
    • Build with AI
    • Content Lake
    • Functions
    • APIs and SDKs
    • Visual Editing
    • Blueprints
    • Platform management
    • Dashboard
    • Studio
    • Canvas
    • Media Library
    • App SDK
    • Content Agent
    • HTTP API
    • CLI
    • Libraries
    • Specifications
    • Changelog
    • User guides
    • Developer guides
    • Courses and certifications
    • Join the community
    • Templates
Studio
Overview

  • Setup and development

    Installation
    Project Structure
    Development
    Hosting and deployment
    Embedding Sanity Studio
    Upgrading Sanity Studio
    Environment Variables
    Using TypeScript in Sanity Studio
    Understanding the latest version of Sanity

  • Configuration

    Introduction
    Workspaces
    Schema and forms
    Conditional fields
    Field Groups
    List Previews
    Connected Content
    Validation
    Initial Value Templates
    Cross Dataset References
    Sort Orders
    Visual editing and preview
    Incoming reference decoration

  • Block Content (Portable Text)

    Introduction
    Configure the Portable Text Editor
    Customize the Portable Text Editor
    Create a Portable Text behavior plugin
    Add Portable Text Editor plugins to Studio
    Common patterns
    Standalone Portable Text Editor

  • Studio customization

    Introduction
    Custom component for Sanity Studio
    Custom authentication
    Custom asset sources
    Diff components
    Form Components
    How form paths work
    Icons
    Favicons
    Localizing Sanity Studio
    New Document Options
    Studio Components
    Studio search configuration
    Focus and UI state in custom inputs
    Real-time safe patches for input components
    Sanity UI
    Studio Tools
    Create a custom Studio tool
    Tools cheat sheet
    Theming

  • Workflows

    The Dashboard tool for Sanity Studio
    Add widgets to dashboard
    Document actions
    Release Actions
    Custom document badges
    Localization
    Content Releases Configuration
    Enable and configure Comments
    Configuring Tasks
    Scheduled drafts
    Scheduled publishing (deprecated)
    Manage notifications

  • Structure builder

    Introduction
    Get started with Structure Builder API
    Override default list views
    Create a link to a single edit page in your main document type list
    Manually group items in a pane
    Dynamically group list items with a GROQ filter
    Create custom document views with Structure Builder
    Cheat sheet
    Structure tool
    Reference

  • Plugins

    Introduction
    Installing and configuring plugins
    Developing plugins
    Publishing plugins
    Internationalizing plugins
    Reference
    Official plugins repo

  • AI Assist

    Installation
    Translation
    Custom field actions
    Field action patterns

  • User guides

    Comments
    Task
    Copy and paste fields
    Compare document versions
    Content Releases
    Scheduled drafts
    View incoming references
    Common keyboard shortcuts

  • Studio schema reference

    Studio schema configuration
    Array
    Block
    Boolean
    Cross Dataset Reference
    Date
    Datetime
    Document
    File
    Geopoint
    Global Document Reference
    Image
    Number
    Object
    Reference
    Slug
    Span
    String
    Text
    URL

  • Studio reference

    Asset Source
    Configuration
    Document
    Document Badges
    Document Actions
    Form
    Form Components
    Hooks
    Structure tool
    Studio Components Reference
    Tools
    Initial Value Templates
    Studio API reference

On this page

Previous

Reference

Next

Span

Was this page helpful?

On this page

  • Properties
  • Options
  • Validation
StudioLast updated November 17, 2025

Slug

A schema type for slugs is typically used to create unique URLs.

Loading...
A typical slug field with title and description

A slug is a unique string (typically a normalized version of title or other representative string), often used as part of a URL. The input form will render an error message if the current slug field is not unique (see note on uniqueness below).

Properties

  • Requiredtype

    Value must be set to slug.

  • Requiredname

    Required. The field name. This will be the key in the data record.

  • title

    Human readable label for the field.

  • hidden

    If set to true, this field will be hidden in the studio. You can also return a callback function to use it as a conditional field.

  • readOnly

    If set to true, this field will not be editable in the content studio. You can also return a callback function to use it as a conditional field.

  • description

    Short description to editors how the field is to be used.

  • initialValue

    The initial value used when creating new values from this type. Can be either a literal value or a resolver function that returns either a literal value or a promise resolving to the initial value.

  • deprecated

    Marks a field or document type as deprecated in the studio interface and displays a user-defined message defined by the single required reason property.

    If you deploy a GraphQL API schema, this property will translated into the @deprecated directive.

Options

  • source

    The name of the field which the slug value is derived from. If a string is provided, it should match the name of the source field in your schema. If a function is provided, the source function is called with two parameters: doc (object - the current document) and options (object - with parent and parentPath keys for easy access to sibling fields).

  • maxLength

    Maximum number of characters the slug may contain when generating it from a source (like a title field) with the default slugify function. Defaults to 200. If you include your own slugify function, or manually enter your slug this option will be ignored.

  • slugify

    Supply a custom override function which handles string normalization. slugify is called with three parameters: input (string), type (object - schema type) and context (object). If slugify is set, the maxLength option is ignored.

  • isUnique

    Supply a custom function which checks whether or not the slug is unique. Receives the proposed slug as the first argument and an options object.

Validation

  • required()

    Ensures that this field exists.

  • custom(fn)

    Creates a custom validation rule.

By default, the slug input will check for uniqueness based on the document type and the path to the slug field. For instance, a document of type article and a document of type product can have the same slug. You can customize this behavior by defining an isUnique function in the field options.

The value of the slug field is stored on the current property.

Input

{
  title: 'Slug',
  name: 'slug',
  type: 'slug',
  options: {
    source: 'title',
    maxLength: 200, // will be ignored if slugify is set
    slugify: input => input
                         .toLowerCase()
                         .replace(/\s+/g, '-')
                         .slice(0, 200)
  }
}

Response

{
  "_type": "slug",
  "current": "this-is-the-title"
}

Custom slugify function

import slugify from 'some-off-the-shelf-slugifier'

async function myAsyncSlugifier(input, schemaType, context) {
  const slug = slugify(input)
  const {getClient} = context
  const client = getClient({apiVersion: '2022-12-07'})
  const query = 'count(*[_type=="movie" && slug.current == $slug]{_id})'
  const params = {slug: slug}
  return client.fetch(query, params).then((count) => {
    console.log('Movies with identical slug', count)
    return `${slug}-${count + 1}`
  })
  return slug
}

//…
// schema field
{
  title: 'Slug',
  name: 'slug',
  type: 'slug',
  options: {
    source: 'title',
    slugify: myAsyncSlugifier
  }
}

Custom isUnique function

By default the isUnique function checks for uniqueness across all documents of the same type. Here's an example of an isUnique function that checks for uniqueness across all documents in your dataset:

import { getPublishedId } from 'sanity';

export async function isUniqueAcrossAllDocuments(
  slug,
  context
) {
  const { document, getClient } = context;
  const client = getClient({ apiVersion: '2025-02-19' });
  const id = document?._id;

  if (!id || !slug?.current) {
    return true;
  }

  const publishedId = getPublishedId(id);

  const params = {
    published: publishedId,
    slug: slug.current,
  };

  const query = groq`!defined(*[
    !sanity::versionOf($published) &&
    slug.current == $slug
  ][0]._id)`;

  const isUnique = await client.fetch(query, params);
  return isUnique || false;
}
import {isUniqueAcrossAllDocuments} from '../lib/isUniqueAcrossAllDocuments'

export default {
  name: 'post',
  type: 'document',
  title: 'Post',
  fields: [
    {
      name: 'title',
      type: 'string',
      title: 'Title'
    },
    {
      name: 'slug',
      type: 'slug',
      title: 'Slug',
      options: {
        isUnique: isUniqueAcrossAllDocuments
      }
    }
  ]
}

Keep in mind that unlike other validator functions that can pass a message, isUnique expects a boolean response.

Custom source function

It's also possible to provide the source as a function, that will be called with a first argument containing the whole document, and a second containing a context object.

{
  title: 'Slug',
  name: 'slug',
  type: 'slug',
  options: {
    // include category if dataset is production
    source: (doc, context) => context.dataset === 'production' ? `${doc.category}-${doc.title}` : doc.title
  }
}

The source function also receives an options object containing the parent object/array, if any. It can be useful if you want to derive the slug from a sibling field instead of a property on the document root:

{
  title: 'Slug',
  name: 'slug',
  type: 'slug',
  options: {
    source: (doc, context) => context.parent.title
  }
}

To query for a document with a given slug, make sure you put the constraint on the current key:

*[_type == "your-document-type" && slugFieldName.current == "your-slug"]
Screenshot of a slug field from Sanity Studio
{
  title: 'Slug',
  name: 'slug',
  type: 'slug',
  options: {
    source: 'title',
    maxLength: 200, // will be ignored if slugify is set
    slugify: input => input
                         .toLowerCase()
                         .replace(/\s+/g, '-')
                         .slice(0, 200)
  }
}
{
  "_type": "slug",
  "current": "this-is-the-title"
}
import slugify from 'some-off-the-shelf-slugifier'

async function myAsyncSlugifier(input, schemaType, context) {
  const slug = slugify(input)
  const {getClient} = context
  const client = getClient({apiVersion: '2022-12-07'})
  const query = 'count(*[_type=="movie" && slug.current == $slug]{_id})'
  const params = {slug: slug}
  return client.fetch(query, params).then((count) => {
    console.log('Movies with identical slug', count)
    return `${slug}-${count + 1}`
  })
  return slug
}

//…
// schema field
{
  title: 'Slug',
  name: 'slug',
  type: 'slug',
  options: {
    source: 'title',
    slugify: myAsyncSlugifier
  }
}
import { getPublishedId } from 'sanity';

export async function isUniqueAcrossAllDocuments(
  slug,
  context
) {
  const { document, getClient } = context;
  const client = getClient({ apiVersion: '2025-02-19' });
  const id = document?._id;

  if (!id || !slug?.current) {
    return true;
  }

  const publishedId = getPublishedId(id);

  const params = {
    published: publishedId,
    slug: slug.current,
  };

  const query = groq`!defined(*[
    !sanity::versionOf($published) &&
    slug.current == $slug
  ][0]._id)`;

  const isUnique = await client.fetch(query, params);
  return isUnique || false;
}
import {isUniqueAcrossAllDocuments} from '../lib/isUniqueAcrossAllDocuments'

export default {
  name: 'post',
  type: 'document',
  title: 'Post',
  fields: [
    {
      name: 'title',
      type: 'string',
      title: 'Title'
    },
    {
      name: 'slug',
      type: 'slug',
      title: 'Slug',
      options: {
        isUnique: isUniqueAcrossAllDocuments
      }
    }
  ]
}
{
  title: 'Slug',
  name: 'slug',
  type: 'slug',
  options: {
    // include category if dataset is production
    source: (doc, context) => context.dataset === 'production' ? `${doc.category}-${doc.title}` : doc.title
  }
}
{
  title: 'Slug',
  name: 'slug',
  type: 'slug',
  options: {
    source: (doc, context) => context.parent.title
  }
}
*[_type == "your-document-type" && slugFieldName.current == "your-slug"]