Implementing redirects
Redirects are a critical component of SEO and site maintenance. While they may appear straightforward at first, improper implementation can lead to complex redirect chains and degraded site performance. Let's go through best practices for implementing redirects with Next.js and Sanity.
Learning objectives
You will create a redirect system that:
- Is a document in Sanity
- Can be managed by your content team
- Won't create a maintenance headache later
Creating the schema
Let's start with your redirect schema first. You want to make this as editor friendly as possible. The goal should be to build a non-technical solution that can be managed by your content team.
If you haven't read the docs (opens in a new tab) yet, do so now.
import { defineField, defineType } from "sanity";
import { LinkIcon } from "@sanity/icons";
export const redirectType = defineType({
name: "redirect",
type: "document",
icon: LinkIcon,
fields: [
defineField({
name: "source",
type: "string",
}),
defineField({
name: "destination",
type: "string",
}),
defineField({
name: "permanent",
type: "boolean",
initialValue: true,
}),
defineField({
name: "isEnabled",
description: "Toggle this redirect on/off",
type: "boolean",
initialValue: true,
}),
],
});
Fetching the redirects
Now we need a way to get these redirects from Sanity. Here's a simple utility function:
import { client } from "@/lib/sanity/client";
export async function getRedirects() {
const redirects = await client.fetch(`
*[_type == "redirect" && isEnabled == true] {
source,
destination,
permanent
}
`);
return redirects;
}
Things to note
- Vercel has a limit of 1,024 redirects in next config
- For large numbers of redirects (1000+), use a custom middleware solution instead
- See Vercel's docs on managing redirects at scale (opens in a new tab) for more details
- Validation is critical
- Invalid redirects can break Builds
- Source paths must start with
/
- Never create circular redirects like
A
->B
->A
Pulling redirects in your next.config
Now we can use Next.js's built-in redirects configuration in next.config.js. This allows us to define redirects that will be applied at build time. Note that redirects defined in next.config.js run before any Middleware, so keep this in mind when structuring your routing logic:
import type { NextConfig } from "next";
import { getRedirects } from "@/lib/sanity/getRedirects";
const redirectsFromSanity = await getRedirects();
const nextConfig: NextConfig = {
async redirects() {
return [
/* other redirects */
...redirectsFromSanity,
];
},
};
export default nextConfig;
Validation rules
Here's the validation logic, yes it's a bit complex but it's worth it to avoid hours of debugging, when your build breaks because of a missing slash.
defineField({
// You can use this for both redirect and source validation
// If you are only going to do redirects internally
name: "source",
type: "string",
validation: (Rule) =>
Rule.required().custom((slug: string | undefined) => {
// Must start with /
if (!slug?.startsWith("/")) {
return "Path must start with /";
}
// Check for invalid characters (excluding valid path chars and :)
const invalidChars = /[^a-zA-Z0-9\-\_\/\:]/g;
if (invalidChars.test(slug)) {
return "Path contains invalid characters";
}
// Validate parameter format - : can only appear directly after /
const invalidParamFormat = /:[^\/]+:/;
if (invalidParamFormat.test(slug)) {
return "Parameters can only contain one : directly after /";
}
// Validate that : only appears after /
const parts = slug.split('/');
for (const part of parts) {
if (part.includes(':') && !part.startsWith(':')) {
return "The : character can only appear directly after /";
}
}
return true;
}),
}),
Pro tips from experience
- Keep an eye on redirect chains, they can cause "too many redirects" errors
- Clean up old redirects periodically
- Consider logging redirects if you need to track usage
- Adjust the cache duration based on how often you update redirects
Next up, you are going to start generating some open graph images using tailwind and Vercel edge functions.