Web Studio is here! An enhanced experience to make it easier to create, preview, and collaborate on your website contentLearn More
When working with Next.js and Agility, you should be using the Agility Next.js SDK, found here. That SDK goes hand in hand with the Agility Fetch SDK, which can be found here. A great reference example for all of the concepts is the Agility Next.js Starter Site - all the code is here on GitHub.
The starter has been updated for the Next.js app router and includes advanced object and path caching.
In Agility a Layout is the object that represents a Page on your website or Digital Solution, so you may see these terms used interchangeably.
Getting content from Agility is done through the 2 SDKs mentioned above. The Agility Next.js SDK handles getting the initial Layout's route data.
The first place to look is the /app/[...slug]/page.tsx
file. You'll notice that this is a React Server Component, which is rendered asynchronously on the server, so you can do any data access in the render method that you need. We are doing a call to getAgilityPage
here and passing in the params
object that we get from Next.js. Taking a look at that method's implementation, we are calling getAgilityPageProps
and sending contentLinkDepth: 0
, signalling that we ONLY want the Layout data and NOT the Component data.
This is a big decision and will determine a lot about how your Components will be implemented. That decision should primarily be determined by where you're going to host the site, and if it supports on-demand re-validation.
If the hosting provider supports on-demand validation, you don't have to wait for the re-validation timeout to expire before live Layout routes are refreshed, meaning your Content team will see much faster updates from Agility to the site.
The next 2 sections will deal with the cases for both types of hosting capabilities.
If you are using an environment (like Vercel and others soon) that supports on-demand re-validation, we recommend using contentLinkDepth: 0
when accessing Layout objects. This means that you will only receive a contentID in the props for the Components on that Layout, however, it means that you will be able to control the caching for that Component content independently from the Layout itself. When Content is changed in Agility, you will be able to instantly invalidate it and see much faster updates in your site.
The object level caching methods that go hand in hand with on-demand re-validation are in the `/lib/cms/` folder of the starter.
Here's the getContentItem
method, which is a wrapper around the base-sdk method of the same name.
export const getContentItem = async <T>(params: ContentItemRequestParams) => {
const agilitySDK = getAgilitySDK()
agilitySDK.config.fetchConfig = {
next: {
tags: [`agility-content-${params.contentID}-${params.languageCode || params.locale}`],
revalidate: cacheConfig.cacheDuration,
},
}
return await agilitySDK.getContentItem(params) as ContentItem<T>
}
You can see from the code in there that we are adding specific tags
to our call to the fetch API with a special next
property. The methods for Content Items, Lists, Layouts and Sitemaps have been similarly wrapped in this project so that we can automatically clear the cache using Agility's webhooks.
Let's take a look at the code we're using the invalidate these cache objects. It's in the /app/api/revalidate/route.tsx
file.
export async function POST(req: NextRequest, res: NextResponse) {
//parse the body
const data = await req.json() as IRevalidateRequest
//only process publish events
if (data.state === "Published") {
//revalidate the correct tags based on what changed
if (data.referenceName) {
//content item change
const itemTag = `agility-content-${data.referenceName}-${data.languageCode}`
const listTag = `agility-content-${data.contentID}-${data.languageCode}`
revalidateTag(itemTag)
revalidateTag(listTag)
console.log("Revalidating content tags:", itemTag, listTag)
} else if (data.pageID !== undefined && data.pageID > 0) {
//page change
const pageTag = `agility-page-${data.pageID}-${data.languageCode}`
revalidateTag(pageTag)
//also revalidate the sitemaps
const sitemapTagFlat = `agility-sitemap-flat-${data.languageCode}`
const sitemapTagNested = `agility-sitemap-nested-${data.languageCode}`
revalidateTag(sitemapTagFlat)
revalidateTag(sitemapTagNested)
console.log("Revalidating page and sitemap tags:", pageTag, sitemapTagFlat, sitemapTagNested)
}
}
return new Response(`OK`, {
status: 200
})
}
The code above is taking a web-hook request from Agility and determining if it's for published content. If so, we then determine if it was a Layout or a Content item (or Component) that was updated.
For Content, we invalidate 2 tags - one for the item itself and one for the whole list that the Content might be part of.
For Layouts, we invalidate the actual item itself as well as the Sitemaps.
On top of this object-level caching, we also have route (or path segment) level caching, meaning that Next.js will only rebuild the route every x
seconds. This rebuild is a much faster operation if the route is built from cached objects.
When we invalidate a cached object, Next.js automatically invalidates the routes that used that object in the first place.
In the case where your hosting environment supports on-demand re-validation, you can set the path re-validation timeout to a larger value.
See the page.tsx
file exports for the extra exports that we found we had to set in addition to the re-validate value.
export const revalidate = cacheConfig.pathRevalidateDuration
export const runtime = "nodejs"
export const dynamic = "force-static"
The 2 env vars that important to note here are:
AGILITY_PATH_REVALIDATE_DURATION
AGILITY_FETCH_CACHE_DURATION
Here's what this means for and end user. When we cache a Layout and its Components separately (by using contentLinkDepth: 0
in our getAgilityPageProps
calls) we can cache the Components for a Layout independently from the Layout itself.
The editor experience becomes so much better with on-demand re-validation!
If you're hosting your site on an environment that doesn't support on-demand re-validation, you can still provide an excellent experience for your content team while providing a high-performing site, with the caveat that editors will have to wait for the path re-validation timeout to elapse before they can changes to live content.
The key value here is the AGILITY_PATH_REVALIDATE_DURATION
env var, which is exported from the page.tsx file.
export const revalidate = cacheConfig.pathRevalidateDuration