Wildcard items have been used for a fair number of Siteco. For the most part, they’ve all had the same purpose. Load an item, of a particular template, from another location within the Sitecore CMS.
A typical scenario is when authors utilize buckets, and the URL might be https://abccompany.com/insights/2023/10/23/a-simple-story
by using a *
(aka wildcard) as an item name underneath insights
. We can then transform that URL into https://abccompany.com/insights/a-simple-story
. But it could also be used to load a-simple-story
from another location entirely, outside of the site structure.
This was often accomplished using a content search based on template, filters, etc. to identify the correct item, get its information, put it into the view model, and then display that data based on the Presentation Details associated with the wildcard item accordingly.
The Problem
When it comes to using a wildcard item in XM Cloud, you don’t have the ability to perform that content search in the traditional sense. You can’t use a content resolver as you aren’t hitting the back-end, not to mention a traditional content resolver doesn’t gain access to the HTTP request. All you have is the ability to use GraphQL queries against Experience Edge. So how do you do it? Well, let’s explore the simplest solution.
The Solution
I will acknowledge that this solution came to pass with assistance from Richard Seal - Sitecore Lead Partner Technical Advocate. As it turned out, both of us were trying to solve the problem at the same time, and throughout our discussion, we explored a few different options, but in the end, this ended up being the simplest. I’m certain other developers will extend this or develop their own method.
Ok, let’s get into it.
The [[...path]].tsx file
The secret to making wildcards work in a Headless NextJs front-end application lies in the [[...path]].tsx
file located in the src/pages
folder of your rendering. If we use the above example here, we need to create an insights folder inside src/pages
and then duplicate the [[...path]].tsx
file into that new folder. Then rename it to the [...path].tsx as we only want it scoped under the /insights
folder. We need to make some modifications inside of said duplicate file, but we’ll get to that.
As the default version will attempt to load pages in this path, depending on the number of pages here, we don’t want it to load all of these pages during build. This can cause errors as the build doesn’t get those items as part of the sitemap build (depending on your environment) but also because the paths for said items will differ.
Modifying getStaticPaths
We will shrink the size of getStaticPaths by removing the sitemapFetcher to prevent loading all the pages during build. Sometimes, you might not want this to be here because you’re loading the pages from another source entirely.
export const getStaticPaths: GetStaticPaths = async (context) => {
let paths: StaticPath[] = [];
let fallback: boolean | 'blocking' = 'blocking';
paths = [];
fallback = 'blocking';
return {
paths,
fallback,
};
};
By using fallback
set to ‘blocking’
we ensure that these pages aren’t loaded during build.
Modifying getStaticProps
Next up, we need to modify the getStaticProps
function to load the layout of our wildcard item. Now, this wildcard item doesn’t need to be in the /insights/*
path, but it’s handy if it is.
The first thing to do is store the path the user is navigating into a new parameter in the context. In our case, we store this in context.params.requestPath
. Then we can set the path of the context to our wildcard item.
Once that’s done, then we can use the sitecorePagePropsFactory
to generate the appropriate layout information from that wildcard item.
export const getStaticProps: GetStaticProps = async (context) => {
if (context.params) {
context.params.requestPath = context.params.path;
context.params.path = [`insights/,-w-,`];
}
const props = await sitecorePagePropsFactory.create(context);
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
};
};
Creating a Component to Display Wildcard Data
With the layout data from the wildcard item being processed, we now need to display the contents of the insights page that the user accessed. Our simplest way to do this is to assign a JSON Rendering to the Presentation Details of the wildcard item. In our case, we called said component, InsightsPage
.
Within the getStaticProps
of this new component, we will now pull the data from the item the user was trying to access.
Using GraphQL, we can query the item in question against Experience Edge (or even a local dev environment) and retrieve any required fields. We then pass this data into the component, and in our case, we simply then pass this data into sub-components that make up the page. It’s often handy, though not required when all of the wildcard pages use the same layout.
export const getStaticProps: GetStaticComponentProps = async (
rendering: any,
layoutData: any,
context: any
) => {
const graphQLClient = new GraphQLClient(config.graphQLEndpoint);
graphQLClient.setHeader('sc_apikey', config.sitecoreApiKey);
const insightPath = context?.params?.requestPath;
const basePath = '/sitecore/unstructured data/insights/';
const itemPath =
basePath + insightPath.pop().replaceAll('-', ' '); // Adjust depending on naming convention
const query = gql`
query {
item(path: "${itemPath}", language: "en") {
id
name
... on InsightPage {
pageTitle {
value
}
body {
value
}
}
}
}
`;
const data = await graphQLClient.request(query);
return {
context: layoutData?.sitecore?.route,
insightData: data,
};
};
And Voila
You can obviously extend the above to meet your needs. Still, the beauty here is that the entire wildcard item loading is all done in the front-end without the need for any complex content resolvers, controllers, content searches, etc. This solution effectively works not only in XM Cloud but also if you have access to a CM/CD environment. So you’ve effectively future-proofed your solution if your client decides to move into XM Cloud in the future if they aren’t already.