đź‘‹ Next.js Conf 2024: Come build, party, run, and connect with us! See all events
Last updated March 24, 2023

Automatic redirects NextJs - Sanity Studio V3

This guide teaches how to add a custom action for Sanity Studio v3. It covers creating the custom action, defining necessary properties, and doing real-time changes to the Content Lake from the action to create the automatic redirects when the path of your document changes.

When doing changes to the slugs or to the fields that determine the path of your document in your live site, you would want to create a redirect that will take the users from the old path to the new path.

For this you will need to:

  1. Add the new redirect schema.
  2. Configure Next to use the redirects from Sanity.
  3. Add a new action that will automatically create the redirects.
  4. Rebuild the site when a new redirect is created.

Adding this will allow you to do it automatically and provide a better editorial experience for the Studio users.

Create the schema.

First you will create the new schema for redirects and add that schema to your studio.

// schemas/redirect
import { defineField } from 'sanity';

export default defineField({
  title: 'Redirect',
  name: 'redirect',
  type: 'document',
  initialValue: {
    permanent: true,
  },
  fields: [
    {
      title: 'from',
      name: 'source',
      type: 'string',
      description: 'The path to redirect from',
      validation: (Rule) =>
        Rule.required().custom((value) => {
          if (typeof value !== 'string') {
            return 'The path must be a string';
          }
          if (value === '/') {
            return 'The root path cannot be redirected';
          }
          if (!value.startsWith('/')) {
            return 'The path must start with a /';
          }
          return true;
        }),
    },

    {
      title: 'to',
      name: 'destination',
      type: 'string',
      description: 'The path to redirect to',
      validation: (Rule) =>
        Rule.required().custom((value) => {
          if (typeof value !== 'string') {
            return 'The path must be a string';
          }
          if (!value.startsWith('/')) {
            return 'The path must start with a /';
          }
          return true;
        }),
    },
    {
      title: 'Permanent',
      name: 'permanent',
      type: 'boolean',
      description: 'Whether the redirect is permanent or temporary',
      validation: (Rule) => Rule.required(),
    },
  ],
});

Configure next to use redirects.

Then you need to set NextJs to use your redirects from Sanity, that is done in the next.config file and define the file to resolve the redirects.

// next.config.js
const { resolveRedirects } = require('./sanity/resolveRedirects');

const nextConfig = {
   // ... all your next config settings,
  async redirects() {
    const redirects = await resolveRedirects();
    return redirects;
  },
};


// /sanity/resolveRedirects.js
const sanity = require('next-sanity');
const config = require('./sanity-config'); 

function resolveRedirects() {
  const sanityClient = sanity.createClient(config);
  return sanityClient.fetch(`
  *[_type == "redirect" && !(_id in path("drafts.**"))]{
        source, destination, permanent
    }`);
}

module.exports = { resolveRedirects };

Now you have everything configured to start creating redirects in Sanity studio, those redirects will impact your site and if you visit a old path, you will be redirect to the new path, and you should see a 307 or 308 redirect response in the networks tab.

After adding redirects to the studio, you will need to rebuild your NextJs site, given that the `resolveRedirects` function will only run at build time.

Give it a try!

Automatic redirects action.

Now that we have everything configured for Next to handle the redirects from Sanity, we can start creating the custom action that will create the automatic redirects.

// sanity/actions/PublishAndRedirect.tsx

import { useState, useEffect } from 'react';
import {
  DocumentActionProps,
  useClient,
  useValidationStatus,
  useDocumentOperation,
} from 'sanity';
import { useToast } from '@sanity/ui';
// Custom function that resolves the path from your document.
import { resolveProductionPath } from '../resolveProductionUrl';


export default function PublishAndRedirect(props: DocumentActionProps) {
  const client = useClient();
  const toast = useToast();
  const { publish } = useDocumentOperation(props.id, props.type);
  const validation = useValidationStatus(props.id, props.type);
  // Check if the document is valid before allowing the user to publish it.
  const isValid =
    !validation.isValidating &&
    validation.validation.filter((v) => v.level === 'error').length === 0;

  const [isPublishing, setIsPublishing] = useState(false);
  const draftPath = resolveProductionPath(props.draft);
  const publishedPath = resolveProductionPath(props.published);

  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]);

  const handlePublish = async () => {
    // This will update the button text
    setIsPublishing(true);
    // Create the redirect
    await client.create({
      _type: 'redirect',
      source: publishedPath,
      destination: draftPath,
      permanent: true,
    });
    
    // Notify the user the redirect was created.
    toast.push({
      closable: true,
      duration: 5000,
      status: 'success',
      title: `Redirect created`,
      description: `Redirect from ${publishedPath} to ${draftPath}`,
    });
    // Perform the publish
    publish.execute();
    // Signal that the action is completed
    props.onComplete();
  };
  if (publishedPath && draftPath && draftPath !== publishedPath) {
    return {
      disabled: !!publish.disabled || !isValid,
      tone: 'positive',
      label: isPublishing ? 'Publishing…' : 'Publish & Redirect',
      onHandle: handlePublish,
    } as const;
  }
  
  return null;
}

Then, you want to add that action to your sanity.config file and also keep the previous actions, to allow users decide if they want to Publish and Redirect or just Publish.

// sanity.config.ts
import PublishAndRedirect from "./sanity/actions/PublishAndRedirect"

export default defineConfig({
  // ... all your previous config.
  document: {
    actions: (prev) => [PublishAndRedirect, ...prev],
  },
});

Action that is added

Now when a user publishes a change to a document which will change the path assigned to it, a new redirect will be created.

Rebuild your site.

For this step, you will need to have access to Vercel Dashboard and create a deploy hook for the branch, so a new build is triggered when a new redirect is created.

Dashboard > settings > git > deploy hooks.

Check documentation for more details.

Then, you want to go to the Sanity dashboard and create a webhook that will be triggered when a new document with the type revalidate is published.

Check documentation for more details.

Wrapping up.

And that's all, now when a user changes the document slug or anything related to the path, a new redirect will be created from the old path to the new path and a build will be triggered in your project to include that redirect.

No more users visiting old paths !

Learn more about redirects https://nextjs.org/docs/api-reference/next.config.js/redirects

Sanity – build remarkable experiences at scale

Sanity Composable Content Cloud is the headless CMS that gives you (and your team) a content backend to drive websites and applications with modern tooling. It offers a real-time editing environment for content creators that’s easy to configure but designed to be customized with JavaScript and React when needed. With the hosted document store, you query content freely and easily integrate with any framework or data source to distribute and enrich content.

Sanity scales from weekend projects to enterprise needs and is used by companies like Puma, AT&T, Burger King, Tata, and Figma.