Next.js is the most advanced Jamstack framework these days, yet it does not provide a smooth solution for running tasks before build time. How can you add pre-build scripts to your pipeline?
Ondrej PolesnyPublished on Aug 24, 2022
In this article, I’ll explain some of the use-cases for having pre-build (or start-up) scripts, show how to implement them and run them before each Next.js build, and provide code samples so you can easily start using the solution in your projects.
Why do we need pre-build scripts?
The site you’re on right now is built with Next.js, and it’s rather large in size. Over time, we encountered multiple occasions when pre-build scripts are helping us simplify the implementation or are the only way to handle some tasks. These are the most common use cases:
- Handling old URL slugs
Next.js is capable of issuing proper redirects, but you need to provide the old and new URLs before running a build. We generate the URLs from the content before each build.
- Generating RSS feed
Next.js can handle generating an RSS feed. It’s just another piece of code, but where to put it? Some articles suggest the index page, which works, but feels like a workaround rather than a proper solution.
- Sitemap excluded content
For every page that appears on our site, we have a checkbox allowing content editors to exclude such pages from the sitemap. We generate a list of these pages and use it as a data source for the relevant plugin.
- Site-wide content
We use some types of content on literally all pages. That includes the basic product info, logo, links, and so on. We use the pre-build scripts to generate a code file that’s available globally in the whole project.
Creating and running pre-build scripts
Running some code before we execute the site build is not that complicated. We simply add a new script that has
pre prefix in
However, this works only for Node.js scripts. When implementing any larger site, you typically use TypeScript, and when we use it for the main site, we want to continue this trend in pre-build scripts too.
To run them, we need to use
ts-node which is a TypeScript execution engine for Node.js. You can install it as any other NPM package:
Note: Depending on your project, you may also need to install
@types/node packages. In advanced scenarios, you can also use a transpiler like Babel.
The pre-build script in
package.json then looks like this:
Now, what’s the
runner.ts code file?
On our site, we used to define the pre-build scripts manually in
package.json, but as we were adding more of them, the pre-build script line got really long and became hard to maintain. For that reason, we wanted to automate the process and added the runner.
Pre-build scripts runner
You can look at the runner as a parent script that takes care of executing every script in a specific folder. It needs to perform the following tasks:
- Load environment variables
- Discover all scripts in a defined folder
- Execute each script and wait for it to finish
The first task is easy. Next.js allows us to use its
loadEnvConfig function, which takes care of loading environment variables for respective environments:
Note: You can also use the classic
dotenv package, but Next.js has its own logic and naming of env files, so it’s just easier to do it the Next.js recommended way.
Then, we need to read all files in a specific directory – in our case, named
pre-build – and find all code files. We want all files that end with the
Note: In our implementation, we also sort the scripts by their name. That allows us to define an order of priority as some scripts require the output of others.
Now, to be able to run these scripts in an automated way, we need to define some rules the scripts have to follow so we can keep adding new scripts without having to adjust the runner code. This is the basic frame for them:
IScriptParams interface is very simple, and its only purpose (for now) is to let scripts use environment variables:
When all scripts use the same frame, we can initialize and run them in a generic fashion from the runner:
Note that we first need to destructure the object with an alias as
This is the full code of the runner:
Note: You can also use the top-level async-await if you target ES2017 and higher.
This runner will search through the
pre-build directory and execute any script it finds.
For example, this is a pre-build script that we use for generating redirects from content:
This script is executed by the pre-build scripts runner, fetches all historically used URL slugs from content, transforms it into the structure requested by Next.js, and saves everything as a JSON file that is available to Next.js config during build time.
In this article, I showed you how pre-build scripts can be useful in building your Next.js site. And indeed, every now and then, while implementing functionality on our own site, we leverage this simple concept of pre-fetching data. I explained some of the most common use-cases and shared the full code of a script runner that can be executed before the actual
next build to process all needed tasks.
If you have any questions about the solution or need a hand with your Next.js site, make sure to join our Discord.