Joint session with Vercel: How to build intelligent storefronts (May 15th)

Next.js Redirect schema with validation

Create Next.js redirects, directly inside of your Sanity Studio with validation

By Roboto Studio, Jono, Hrithik Prasad, Snehil Tripathi & Aayush Bharti


redirect.ts

import { defineField, defineType, SanityDocumentLike } from "sanity";
import { LinkIcon } from "@sanity/icons";

function isValidInternalPath(value: string | undefined) {
  if (!value) {
    return "Value is required";
  } else if (!value.startsWith("/")) {
    return "Internal paths must start with /";
  } else if (/[^a-zA-Z0-9\-_/:]/.test(value)) {
    return "Source path contains invalid characters";
  } else if (/:[^/]+:/.test(value)) {
    return "Parameters can only contain one : directly after /";
  } else if (
    value.split("/").some((part) => part.includes(":") && !part.startsWith(":"))
  ) {
    return "The : character can only appear directly after /";
  }
  return true;
}

function isValidUrl(value: string | undefined) {
  try {
    new URL(value || "");
    return true;
  } catch {
    return "Invalid URL";
  }
}

export const redirectType = defineType({
  name: "redirect",
  title: "Redirect",
  type: "document",
  icon: LinkIcon,
  validation: (Rule) =>
    Rule.custom((doc: SanityDocumentLike | undefined) => {
      if (doc && doc.source === doc.destination) {
        return ["source", "destination"].map((field) => ({
          message: "Source and destination cannot be the same",
          path: [field],
        }));
      }

      return true;
    }),
  fields: [
    defineField({
      name: "source",
      type: "string",
      validation: (Rule) => Rule.required().custom(isValidInternalPath),
    }),
    defineField({
      name: "destination",
      type: "string",
      validation: (Rule) =>
        Rule.required().custom((value: string | undefined) => {
          const urlValidation = isValidUrl(value);
          const pathValidation = isValidInternalPath(value);

          if (urlValidation === true || pathValidation === true) {
            return true;
          }
          return typeof urlValidation === "boolean"
            ? urlValidation
            : pathValidation;
        }),
    }),
    defineField({
      name: "permanent",
      description: "Should the redirect be permanent (301) or temporary (302)",
      type: "boolean",
      initialValue: true,
    }),
    defineField({
      name: "isEnabled",
      description: "Toggle this redirect on or off",
      type: "boolean",
      initialValue: true,
    }),
  ],
});

This is for folks who are looking to implement redirects within Next.js and want to create a document type with validation. It allows clients to be able to create redirects within Sanity Studio itself. Original content was taken from Implementing redirects lesson from Sanity Learn.

We originally built a basic version of this validation. Shoutout to Simeon, who went overboard with SanityDocumentLike. It's much nicer to use and less prone to creating redirect loops.

If you're looking to implement this yourself, click the link on the lesson above, save yourself a couple of hours worth of setup, and read through the lesson.

Contributors

Roboto Studio

The best editorial experiences on the web

Visit Roboto Studio's profile

Jono

Founder @ Roboto Studio

Visit Jono's profile

Hrithik Prasad

Technical Lead at Roboto Studio

India

Visit Hrithik Prasad's profile

Snehil Tripathi

Project manager, full time cat whisperer

Visit Snehil Tripathi's profile

Aayush Bharti

Frontend Developer @ Roboto Studio

India

Visit Aayush Bharti's profile