Content Editors in Sitecore frequently utilize the capability to independently establish redirects, without requiring assistance from developers. In conventional Sitecore .NET MVC projects, this functionality was readily accessible through the 301 Redirect Module and its integrated redirect features.
However, we faced a hurdle during the transition from a Sitecore MVC project to a Headless + Next.js configuration. Our objective was to maintain the use of Sitecore redirects and enable Content Editors to autonomously manage redirects with the same ease, but this proved to be less straightforward than anticipated.
In a Headless/JSS Sitecore setup, the challenge arises from the inability to utilize the traditional 301 Redirect Module (or any backend logic) as one would in a .NET MVC Sitecore site. This is because Sitecore does not directly manage incoming HTTP requests from clients; instead, these requests are handled by the JS application.
As a result, we need a solution capable of managing these requests and searching for the redirects configured by editors in the Sitecore CMS. This blog will explore how to implement editor-controlled redirects using Next.js.
The blog is divided into two sections: the first section focuses on setting up redirects in Sitecore, while the second section elaborates on the implementation of redirects in Next.js.
Sitecore Content Tree
- We won’t delve deeply into the first section, assuming a level of familiarity with how redirects operate in Sitecore.
- The redirect template includes fields such as sourceUrl, redirectItem, redirectUrl, and isPermanent.
- The Redirect Folder template is devoid of content and inherits properties from the standard folder template.
- Once this framework is in place, one or two redirect items can be generated in Sitecore, as demonstrated below.
Moving to the Next.js part,for efficient handling of redirects, we must integrate the logic into the Next.js application. GraphQL will be utilized to search for redirects based on the Source URL.
- To achieve this, a new function named sitecoreRedirect is established under src/lib/sc-redirect in the Next.js application.
import { gql, GraphQLClient } from ‘graphql-request’;
import { Redirect } from ‘next’;
import config from ‘temp/config’;
const isUrlValid = (url: string) => {
try {
new URL(url);
return true;
} catch (err) {
return false;
}
};
export default async function sitecoreRedirect(sourceUrl: string): Promise
// Get the required values from configuration
const endpoint = config.graphQLEndpoint;
const apiKey: string = process.env.SITECORE_API_KEY ?? ”;
const pathId: string = process.env.SC_REDIRECT_PATH_ID ?? ”;
const templateId: string = process.env.SC_REDIRECT_TEMPLATE_ID ?? ”;
const host: string = process.env.PUBLIC_URL ?? ”;
// Return undefined if not all required configuration values are present
if (!endpoint || !apiKey || !pathId || !templateId || !host) {
return undefined;
}
const graphQLClient = new GraphQLClient(endpoint);
graphQLClient.setHeader(‘sc_apikey’, apiKey);
// Declaration of the GraphQL query that will search a redirect by source URL
const query = gql`
query SearchRedirect($sourceUrl: String!, $pathId: String!, $templateId: String!) {
search(
where: {
AND: [
{ name: “_path”, value: $pathId, operator: CONTAINS }
{ name: “_template”, value: $templateId, operator: CONTAINS }
{ name: “Requested Url”, value: $sourceUrl }
]
}
first: 1
) {
total
pageInfo {
endCursor
hasNext
}
results {
redirectItem: field(name: “Redirect To Item”) {
jsonValue
}
redirectUrl: field(name: “Redirect To Url”) {
value
}
isPermanent: field(name: “Response Status Code”) {
jsonValue
}
}
}
}
`;
// GraphQL query is executed with the required parameters
const result: any = await graphQLClient.request(query, {
sourceUrl: sourceUrl,
pathId: pathId,
templateId: templateId,
});
let redirectUrl = ”;
// If no redirect found, return undefined
if (result.search.total === 0) {
return undefined;
}
// Retrieve the redirect URL
if (result.search.results[0].redirectItem.jsonValue?.url) {
if (isUrlValid(result.search.results[0].redirectItem.jsonValue?.url)) {
const item = new URL(result.search.results[0].redirectItem.jsonValue.url).pathname;
redirectUrl = `${host}${item}`;
} else {
redirectUrl = result.search.results[0].redirectItem.jsonValue?.url;
}
} else if (result.search.results[0].redirectUrl.value) {
redirectUrl = result.search.results[0].redirectUrl.value;
}
if (!redirectUrl) {
return undefined;
}
// Set the isPermanent flag depending on the isPermanent checkbox in Sitecore redirect item
const isPermanent = result.search.results[0].isPermanent.jsonValue.value as boolean;
// Return the Redirect object
return {
permanent: isPermanent,
destination: redirectUrl,
};
}
- The GraphQL query necessitates three parameters: sourceUrl, pathId, and templateId. While sourceUrl is derived from the client request, pathId and templateId must be sourced from the Redirect root folder in your content for pathId, and the Template ID of the Redirect template for templateId. These IDs are retrieved from environment variables SC_REDIRECT_PATH_ID and SC_REDIRECT_TEMPLATE_ID, respectively, which should be specified in the .env file.
- Once this function and the requisite variables are set up, it should be invoked from the appropriate location in the code. When a client requests a Sitecore page, the code within the [[…path]].tsx file under src/pages is executed. This is the point where the function call for redirects should occur, prior to the actual page rendering or in instances where no page is found, just before automatic redirection to a 404 page.