Jon Burbridge
Senior Solution Architect at Sanity.io
A Sanity function that tracks slug changes and creates redirects automatically.
import {defineType, defineField, type Rule, type Slug} from 'sanity'
// Shared validation for our redirect slugs
const slugValidator = (rule: Rule) =>
rule.required().custom((value: Slug) => {
if (!value || !value.current) return "Can't be blank"
if (!value.current.startsWith('/')) {
return 'The path must start with a /'
}
return true
})
export const redirectType = defineType({
name: 'redirect',
title: 'Redirect',
type: 'document',
description: 'Redirect for next.config.js',
fields: [
defineField({
name: 'source',
type: 'slug',
validation: (rule: Rule) => slugValidator(rule),
}),
defineField({
name: 'destination',
type: 'slug',
validation: (rule: Rule) => slugValidator(rule),
}),
defineField({
name: 'permanent',
type: 'boolean',
}),
],
// null / false makes it temporary (307)
initialValue: {
permanent: true,
},
})import {createClient} from '@sanity/client'
import {documentEventHandler} from '@sanity/functions'
export const handler = documentEventHandler(async ({context, event}) => {
const client = createClient({
...context.clientOptions,
useCdn: false,
apiVersion: '2025-05-08',
})
const {beforeSlug, slug} = event.data
if (!slug || !beforeSlug) {
console.log('No slug or beforeSlug')
return
}
if (slug === beforeSlug) {
console.log('Slug did not change')
return
}
// check if redirect already exists
const existingRedirect = await client.fetch(
`*[_type == "redirect" && source.current == "${beforeSlug}"][0]`,
)
if (existingRedirect) {
console.log(`Redirect already exists for source ${beforeSlug}`)
return
}
// check for loops
const loopRedirect = await client.fetch(
`*[_type == "redirect" && source.current == "${slug}" && destination.current == "${beforeSlug}"][0]`,
)
if (loopRedirect) {
console.log('Redirect loop detected')
return
}
const redirect = {
_type: 'redirect',
source: {
current: beforeSlug,
},
destination: {
current: slug,
},
permanent: true,
}
try {
const res = await client.create(redirect)
console.log(`🔗 Redirect from ${beforeSlug} to ${slug} was created ${JSON.stringify(res)}`)
} catch (error) {
console.log(error)
}
})import {defineBlueprint, defineDocumentFunction} from '@sanity/blueprints'
export default defineBlueprint({
resources: [
defineDocumentFunction({
type: 'sanity.function.document',
name: 'auto-redirect',
src: './functions/auto-redirect',
memory: 2,
timeout: 30,
event: {
on: ['publish'],
filter: 'delta::changedAny(slug.current)',
projection: '{"beforeSlug": before().slug.current, "slug": after().slug.current}',
},
}),
],
})The Problem: Managing URL redirects manually becomes a maintenance nightmare as your site grows. When content editors change slugs, move pages, or restructure your site, you need redirects to preserve SEO rankings and prevent broken links. Without automation, you're stuck maintaining redirect lists in multiple places, risking inconsistencies and errors.
The Solution: This function automatically creates and manages redirects when document slugs change in Sanity. It watches for slug modifications and ensures proper redirect handling - all without manual intervention.
Quick Start
View full instructions and source code at github.com/sanity-io/sanity/tree/main/examples/functions/auto-redirect
Initialize blueprints if you haven't already:
npx sanity blueprints init
Add the function to your project:
npx sanity blueprints add function --example auto-redirect
Deploy to production:
npx sanity blueprints deploy
How It Works
When a document's slug field changes, the function automatically:
Key Benefits
Technical Implementation
The function uses Sanity's event-driven architecture to monitor document changes. It's built with:
Perfect For
Frontend Implementation
Every frontend framework handles redirects differently - some use configuration files, others use middleware, and the specifics vary widely. The auto-redirect function creates the redirect documents in Sanity, but you'll need to implement how your frontend consumes them.
For detailed guidance on implementing redirects with your specific framework (Next.js, Nuxt, Remix, Astro, etc.), check out the comprehensive Managing Redirects with Sanity guide. This guide covers:
For complete implementation details and the function source code, visit the GitHub repository.
Senior Solution Architect at Sanity.io
Automatically track when content was first published with a timestamp that sets once and never overwrites, providing reliable publication history for analytics and editorial workflows.
Go to First Published Timestamp FunctionAI-powered automatic tagging for Sanity blog posts that analyzes content to generate 3 relevant tags, maintaining consistency by reusing existing tags from your content library.
Go to Automatically tag blog postsOn document publish, send a Slack notification so your team is informed
Go to Notify your team via Slack on publishCreate, preview, and send Klaviyo campaigns without ever leaving Sanity Studio"
Go to Klaviyo campaigns without leaving your Studio