Set slug on publish with referenced field value
Set slug on publish document action
By Ken Eucker
sanity.json
{
"parts": [
{
"implements": "part:@sanity/base/document-actions/resolver",
"path": "./schemas/documentActions.js"
}
]
}documentActions.js
// schemas/documentActions.js
import defaultResolve, {
PublishAction,
} from 'part:@sanity/base/document-actions';
import SetSlugAndPublishAction from "./actions/setSlugAndPublishAction.js"
/// Publish Actions
export default function useDocumentActions(props) {
/// Leave the code below commented out to run this action for all documents
/// Uncomment the code below to restrict this action to specific documents only
// if (["tag"].indexOf(props.type) !== -1) {
return defaultResolve(props).map((Action) =>
Action === PublishAction ? SetSlugAndPublishAction : Action
);
// }
// return defaultResolve(props)
}setSlugAndPublishAction.js
// schemas/actions/setSlugAndPublishAction.js
import {useState, useEffect} from 'react'
import {useDocumentOperation} from '@sanity/react-hooks'
import sanityClient from '@sanity/client'
const sanityClientConfig = {
projectId: process.env.SANITY_STUDIO_API_PROJECT_ID,
dataset: process.env.SANITY_STUDIO_API_DATASET,
token: process.env.SANITY_STUDIO_API_TOKEN,
useCdn: true,
}
export default function SetSlugAndPublishAction(props) {
const {patch, publish} = useDocumentOperation(props.id, props.type)
const [isPublishing, setIsPublishing] = useState(false)
useEffect(() => {
// if the isPublishing state was set to true and the draft has changed
// to become `null` the document has been published
if (isPublishing && !props.draft) {
setIsPublishing(false)
}
}, [props.draft])
return {
disabled: publish.disabled,
label: isPublishing ? 'Publishing…' : 'Publish',
onHandle: async () => {
// This will update the button text
setIsPublishing(true)
/// Get the sanity client for fetching referenced field values
const client = sanityClient(sanityClientConfig)
/// Set the initial slug value to the name field
let slug = props.draft.name
/// Set the slug based on the document type
switch (props.type) {
case 'tag':
/// The type of the reference field to get the value from
const referenceNameType = 'category'
/// Query for the referenced "category" and return the "name" field of that referenced document
const query = `*[_type == "${referenceNameType}" && _id == $nameRef][0] {name}`
/// Start with the "category: reference id in the draft
let referenceName = props.draft.category._ref
/// Fetch the category referenced in this document
await client.fetch(query, {nameRef: referenceName}).then(category => {
/// Set the referenceName to the category.name field
referenceName = !!category ? category.name : referenceName
})
/// For the "tag" type document, set the slug to [category.name]-[type]-[number]
slug = `${referenceName}-${props.type}-${props.draft.number}`
/// Set name field for the "tag" type document to be the same value as the slug
patch.execute([{set: { name: slug.toLowerCase() }}])
break;
default:
/// Doing nothing? Consider deleting this switch statement to simplify your code.
break;
}
/// Set the slug field of this document
patch.execute([{set: { slug: { _type: 'slug', current: slug.toLowerCase() }}}])
// Perform the publish
publish.execute()
// Signal that the action is completed
props.onComplete()
}
}
}What this code does, in order of operations, is the following:
- Loads the documentActions.js file from within the sanity.json configuration file. See sanity.json.
- Sets the publish action for the configured documents (all, by default) to the new SetSlugAndPublishAction method. See documentActions.js.
- Upon pressing the publish button in sanity studio, the SetSlugAndPublishAction method will query your configured sanity dataset for the coded reference field and set the name of that referenced document in the slug field for the publishing document.
This code makes the following assumptions about your code, which you can modify to meet your schema's needs, using simple field names for the sake of education:
- A document named "tag" is present in your schema.
- The "tag" document has these three fields:
number,category, andslug. - The "category" field within the "tag" document is a reference to a "category" document.
- A document named "category" is present in your schema.
- The "category" document has a
namefield within it, oftypestring, and presumably aslugfield. - All documents defined in the schema have a
slugandnamefield.
My use case for this code was to meet the following needs:
- To generate a slug for a given document that was made up of values within that document, upon publishing the document.
- To include in the slug a "category-like" name and a number -- derived from fields within that document.
- For the "category-like" name to come from a field of type reference within the document.
- For the slug to contain the more readable "name" of the "category-like" field within the document, not the "_ref" value.
Your mileage may vary.
This code example could have been simplified by setting the slug in the same way for all document types. In conjunction with that simplification, the commented-out code in the documentActions.js file could be an easier way to separate the logic between different document types and the way that the slug field is set upon publish, or to only include this publish action with a single document type.
Contributor

Ken Eucker
Software Engineer and Bicycling Enthusiast