Is your Next.js site getting larger, and URLs are getting out of hand? How can you ensure URL consistency, observe editors’ changes and issue proper redirects if URL slugs change?
Ondrej PolesnyPublished on Sep 14, 2022
In my previous article, I explained how URL building works and why it’s important to preserve old URLs when they change. I also introduced a custom element for Kontent.ai that you can directly plug into your project and track all URL slug changes. However, the last bit of the proposed solution does not work in some cases in the newest version of Next.js, so in this article, I will show you a more robust and functional way of handling URLs and redirects for all rendering modes of Next.js.
The future-proof way of handling URLs
There are three components to the successful handling of URLs on any website project:
- Building URLs to content in the website implementation
- Observing URL slug changes
- Implementing a robust redirection mechanism
Building URLs to Content in the Website Implementation
A good content platform allows you to link content items without using URLs. Just like the link at the top of this article is not defined by using a URL but links to the other content item directly.
Not using URLs directly helps a lot with consistency as the platform can warn an editor when they’re about to delete a content item other items reference, but also ensures usable linking of items among multiple channels.
URLs are tightly coupled with the website implementation and type of content:
|Website home||Type of content||Content item|
The consistency of URLs is a vital component of any website, and as such, it should be centralized to minimize the potential for editor and developer errors. For example, this is how we handle it:
We use this map of URL structure to generate any URL to any content with no exceptions:
This has multiple benefits:
- You can’t experience situations when a URL to a blog post is correct on one page but is not working on another.
- You quickly find missing URL structures as the build fails with a meaningful message in that case.
- The structure of the content (how an editor thinks about content and builds it) is not directly dependent on URL structure (how content is displayed on the website) which is a major benefit on larger sites and the basic concept of headless architecture.
- You easily find unused content types, which helps to keep your project organized.
Observing URL Slug Changes
When an editor creates and publishes a page, the search engines index the URL, and from that moment on, we must ensure it always exists. I explained the problem in detail in my previous article, so here I’ll just summarize the solution.
In Kontent.ai, you can use a custom element for keeping track of URL slug changes:
It watches over the URL slug field, and when it gets changed, it automatically stores the previous value. In code, you get both the current URL slug and the history:
Implementing a Robust Redirection Mechanism
Now that we get all the information from the content platform, we can switch to the Next.js implementation.
Here, the problem is that handling the redirect on the page level is not working for pre-built pages. Let’s take blog posts as an example:
|Current URL slug||History|
|Blog post 1||blog-post-1|
|Blog post 2||blog-post-2||shiny-blog-post|
You see, before building the site, we have the knowledge of all the slugs of all the blog posts, including the history. Therefore, we want to use the
fallback: false mode, which instructs Next.js to pre-build everything. We hand over all the slugs, including the history, to the
getStaticPaths method as that defines the existing paths:
So far so good. But when we get to
getStaticProps, we need to handle the redirect:
|URL slug||Render a page?|
|shiny-blog-post||no -> redirect to blog-post-2|
We can use this code to redirect from
getStaticProps to another page:
But because we’re using the
fallback: false mode, all pages in the blog are being resolved during build time, and we’ll get the following error:
Error: 'redirect' can not be returned from getStaticProps during prerendering
The reason is that Next.js does not allow prerendering pages that just redirect somewhere. The best practice is to handle these redirects in
Solution 1: Switch Mode to fallback: blocking
You can solve this problem by switching the mode to
fallback: 'blocking’, which accepts any URL slug and attempts to render a page during runtime, but it comes with three disadvantages:
- You need to exclude the history URL slugs from
getStaticPathsso that Next.js won’t try to render them during pre-building.
- You need to handle 404 cases, so your
getStaticPropsimplementation must first verify the URL slug to see if a page should exist at all.
- The page resolution for all slugs except the ones provided in
getStaticPathshappens at runtime.
This can be a nice workaround for small to medium size websites but not an ultimate solution to the problem.
Solution 2 (Better): Pre-build the Redirects
If we know all the historic slugs before running a build, why waste runtime resources on resolving them?
Next.js has a redirects mechanism; the only problem is that it requires the list of URLs in
next.config.js prior to running a build:
redirects section is a function, so we can pre-build all the URLs into a JSON file and load it in the config. We’ll do this in five steps:
- Add a pre-build script
- Load historic URL slugs
- Generate URLs in the structure Next.js requires
- Save all in a JSON file
- Load the file in
I described the pre-build scripts in another article, so here we’ll just focus on adding the actual functionality.
First, we’ll load the historic URL slugs. Because we’re generating the redirects file for the whole site, we need to handle all used content types here. This is tricky as the codenames of relevant elements may vary among content types. The best practice is to keep the URL slug and URL slug history elements’ codenames consistent among your content model - we use
url_slug_history, respectively. In that case, we can fetch the data using a single Kontent.ai query:
Note: You can adjust codenames of both content types and their elements either in the platform UI or via API
Then, we need to transform the data into the Next.js accepted structure:
Note that we need to
JSON.parse the element value as it’s a JSON encoded array. Then, depending on the used URL building mechanism, we need to build the source and destination URLs from the slugs. On our site, we’re using the function displayed at the beginning of this article –
getUrlPathByContentType – which finds the relevant record in the URLs map by content type.
Then, we save the list to a physical file:
This is the full pre-build script code for reference:
When you run the pre-build script, it fetches the old URL slugs and generates the
/static/redirects.json file that looks like this:
Finally, we implement the
redirects() function to load that JSON file in the
When you run the
next build, Next.js will load the redirects from the
/static/redirects.json file and issue proper redirects on runtime.
In this article, I explained the three components of future-proof URL handling on large Next.js sites. I outlined the problems with redirects on pre-generated sites and provided a robust solution for handling redirects in an automated way using pre-build scripts. With this code in place, your application is ready for URL slug changes and ensures your pages keep their ranks.
If you’re building a Next.js site with Kontent.ai, join our community on Discord to discuss your experience and get help from fellow developers (including me :-)).