👋 Next.js Conf 2024: Come build, party, run, and connect with us! See all events

Slug

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

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

  • REQUIREDtypestring

    Value must be set to slug.

  • REQUIREDnamestring

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

  • titlestring

    Human readable label for the field.

  • hiddenboolean | function

    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.

  • readOnlyboolean | function

    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.

  • descriptionstring

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

  • initialValueInitialValueOrResolverFunction

    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{ reason: String }

    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

  • sourcestring | function

    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).

  • maxLengthnumber

    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.

  • slugifyfunction

    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.

  • isUniquefunction

    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

Learn more about validation
  • required()function

    Ensures that this field exists.

  • custom(fn)function

    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)
  }
}

Output

{
  "_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:

// /lib/isUniqueAcrossAllDocuments.js

// Note: this assumes that every document that has a slug field
// have it on the `slug` field at the root
export async function isUniqueAcrossAllDocuments(slug, context) {
  const {document, getClient} = context
  const client = getClient({apiVersion: '2022-12-07'})
  const id = document._id.replace(/^drafts\./, '')
  const params = {
    draft: `drafts.${id}`,
    published: id,
    slug,
  }
  const query = `!defined(*[!(_id in [$draft, $published]) && slug.current == $slug][0]._id)`
  const result = await client.fetch(query, params)
  return result
}
// post.js
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
      }
    }
  ]
}

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"]

Was this article helpful?