📦 How to package Next.js application into a single executable using PKG2024-10-23
In some cases one might need to publish a Next.js app as a single executable. It’s not as nice as shipping Electron application with Next.js, as I described in my previous article, but it totally gets the job done:
- dependency-free executable that can be distributed to end users
- and the size is much smaller
Users of the app just need to run the executable and open the browser.
In my case I was using it to run some performance measurements on target machines with the ability to interact with measurement tool via web interface. Pretty neat.
Start with installing of the PKG tool. The tool itself has been discontinued, so I will use a fork:
$ npm install @yao-pkg/pkg
Then add following to your package.json
:
{
"name": "next-pkg",
"bin": "server.js",
"scripts": {
"build": "yarn build:next && yarn build:fix && yarn build:pkg",
"build:next": "next build",
"build:fix": "sed -i '' 's/process.chdir(__dirname)//' .next/standalone/server.js",
"build:pkg": "cd .next/standalone && pkg . --compress=GZip --sea",
"open": "./out/next-pkg"
},
"pkg": {
"assets": [
".next/**/*",
"public/**/*.*"
],
"targets": [
"node22-macos-arm64"
],
"outputPath": "../../out"
}
}
This package.json
file will be copied into standalone build, hence the weird paths.
If you're in monorepo, server will be placed one more level down, so:
.next/standalone/server.js
will become.next/standalone/%MONOREPO_FOLDER_NAME%/server.js
"outputPath": "../../out"
should be"outputPath": "../../../out"
.
Next.js standalone build comes with the server, and one line there needs to be fixed in order to work from packaged executable.
"build:fix": "sed -i '' 's/process.chdir(__dirname)//' .next/standalone/server.js"
Now let’s configure the Next.js itself:
export default {
output: 'standalone',
outputFileTracingIncludes: {
'*': ['public/**/*', '.next/static/**/*'],
},
};
This will copy necessary static and public files into the standalone build.