Feb 22, 2023
Overview
One of the most requested features by Content Editors in Sitecore is to be able to set up redirects by themselves, without developer/IT team intervention. In a traditional “headful” Sitecore .NET MVC project, this was easily achieved by using the 301 Redirect Module or by using SXA and its built-in redirect features. Modern headless/
In a Headless/
What do you need?
Tested with Sitecore with JSS version 19. (It should work in earlier versions, but I haven’t tested them.)
Make sure GraphQL is enabled in your Sitecore JSS site.
Next.js for the head app.
In the Next.js app, make sure you install the graphql-request package using
npm install graphql-request
.
Solution
For this solution, we are going to create a set of templates in Sitecore, so editors can create and modify redirects from the CMS.
The Redirect template contains the following fields (for this example, all fields are inserted under a template section called “Data”):
Name | Type | Title (for editors) | Standard value |
---|---|---|---|
sourceUrl | Single-Line Text | Source URL | |
redirectItem | Droptree | Redirect Item | |
redirectUrl | Single-Line Text | Redirect URL | |
isPermanent | Checkbox | Is a Permanent Redirect | Checked |
The Redirect Folder template is empty (based on the /sitecore/
template) but it has configured in Standard Values the insert options for itself (so you can create more redirect folders) and the Redirect template.
Once you have these infrastructural templates set up, you can create the root Redirect Folder (you can call it “Redirects”) anywhere in your content tree, and that will allow editors to create the redirects inside of that folder.
To create a redirect, select the Redirects folder, and click on the Redirect button in the right panel:
Editors can enter a Source URL, that is the URL that has to be matched, so it is redirected to somewhere else, and they can either select a page item in the site in the redirectItem
field, or write an absolute URL in the redirectUrl
field:
The image above depicts an internal redirect.
The image above depicts an external redirect.
To actually handle the redirect, we need to include the logic in the head app. We are going to use GraphQL to search the redirect by Source URL. Create a new function called sitecoreRedirect
under src/
in your Next.js app:
src/lib/sc-redirect/index.ts
import { gql, GraphQLClient } from ‘graphql-request’;
import { Redirect } from ‘next’;
import config from ‘temp/config’;
export default async function sitecoreRedirect(sourceUrl: string): Promise<Redirect | undefined> {
// 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 ?? ‘’;
// Return undefined if not all required configuration values are present
if (!endpoint || !apiKey || !pathId || !templateId) {
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: “sourceUrl”, value: $sourceUrl }
]
}
first: 1
) {
total
pageInfo {
endCursor
hasNext
}
results {
redirectItem: field(name: “redirectItem”) {
jsonValue
}
redirectUrl: field(name: “redirectUrl”) {
value
}
isPermanent: field(name: “isPermanent”) {
jsonValue
}
}
}
}
`;
// GraphQL query is executed with the required parameters
const result = 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) {
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,
};
}
This function contains the GraphQL query inline, but you can also separate it into its own .graphql
file to make the code more readable.
As you can see in the GraphQL query, it takes 3 parameters: sourceUrl
, pathId
and templateId
. The sourceUrl
will come from the client request, but you will need to get the IDs of the Redirect root folder in your content for the pathId, and the Template ID of the Redirect template for the templateId. In this specific example, we are retrieving the path ID and template ID from environment variables SC_REDIRECT_PATH_ID
and SC_REDIRECT_TEMPLATE_ID
. In your .env
file (or Environment Variables in Vercel), make sure you have these variables, and copy-paste the IDs to the variable assignments without curly braces and without dashes.
Once you have this function and the required variables in place, you need to call it from the right place in the code. When a client performs a request to a Sitecore page, it executes the code in the [[…path]].tsx
file under src/pages
. So this is the place where the function call for redirects should be performed BEFORE the actual page is rendered (or, in this case, when no page is found and before it is automatically redirected to a 404 page.) To achieve this, we must place the code in either the getStaticProps
or getServerSideProps
function call, depending on if you implemented the site using either SSG+ISR (Static Site Generation + Incremental Static Regeneration) or SSR (Server-Side Rendering). The following code snippet is for when you have the site implemented with SSG+ISR:
[[...path]].tsx with SSG+ISR
// Include the import of the redirect function at the start of the file where your imports reside
import sitecoreRedirect from ‘lib/sc-redirect’;
// … here goes the rest of the code …
export const getStaticProps: GetStaticProps = async (context) => {
const props = await sitecorePagePropsFactory.create(context);
// If a page does NOT exist, then look for a redirect
if (props.notFound && context.params?.path) {
let sourceUrl = ‘/‘;
// Path elements can come in as an array of each path element
if (Array.isArray(context.params.path)) {
const pathArray = context.params.path as string[];
sourceUrl += pathArray.join(‘/‘);
} else {
sourceUrl += context.params.path;
}
// Our redirect function is called here
props.redirect = await sitecoreRedirect(sourceUrl);
}
// If the redirect is found, here it is performed
if (props.redirect) {
return {
redirect: props.redirect,
};
}
return {
props,
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 5 seconds
revalidate: 5, // In seconds
notFound: props.notFound, // Returns custom 404 page with a status code of 404 when true
};
};
For SSR sites, it should be something similar but in the getServerSideProps
function.
Conclusion
This solution should help when your JSS/Headless site is not using SXA and you need to implement editor-controlled redirects in your site. But if you are creating a new JSS/Headless site from scratch, create it inside a Headless SXA Tenant even if you are not going to use the Headless SXA components in your site. At least you’ll have built-in redirects and sitemap.xml functionality out-of-the-box.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.