electron-builder and electron-packager are tools designed to package and distribute Electron-based desktop applications across Windows, macOS, and Linux. They handle bundling the Chromium runtime, Node.js, and your application code into installable formats like .exe, .dmg, or .AppImage. pkg, on the other hand, focuses on compiling Node.js projects into standalone executables without the Chromium overhead, making it ideal for CLI tools or server-side scripts that need to run on machines without Node.js installed. While the Electron tools target rich GUI experiences, pkg targets lightweight, headless distribution.
When shipping JavaScript applications to end users, the choice of packaging tool depends heavily on whether you are building a graphical desktop app or a command-line utility. electron-builder, electron-packager, and pkg solve different parts of this puzzle. Let's break down how they handle the complexities of distribution, native modules, and updates.
electron-builder is designed specifically for Electron apps.
.exe, .dmg) ready for end users.// electron-builder: package.json config
{
"build": {
"appId": "com.example.app",
"win": {
"target": "nsis"
}
}
}
electron-packager also targets Electron apps but is more bare-bones.
// electron-packager: CLI usage
electron-packager ./app MyApp --platform=win32 --arch=x64
// Outputs: MyApp-win32-x64/MyApp.exe
pkg targets standard Node.js projects, not Electron.
// pkg: package.json config
{
"pkg": {
"targets": [ "node18-win-x64" ],
"outputPath": "dist"
}
}
electron-builder relies on a centralized config.
package.json or a separate YAML file.// electron-builder: electron-builder.yml
appId: com.example.app
mac:
category: public.app-category.productivity
notarize: true
electron-packager uses CLI flags or an API object.
// electron-packager: API usage
const packager = require('electron-packager');
await packager({
dir: './app',
name: 'MyApp',
platform: 'darwin',
arch: 'x64'
});
pkg uses a pkg section in package.json.
// pkg: package.json scripts
{
"scripts": {
"build": "pkg ."
},
"pkg": {
"assets": "views/**/*"
}
}
electron-builder rebuilds native modules automatically.
node-gyp dependencies and compiles them for Electron's Node version.// electron-builder: Handles native deps internally
// No extra command needed if configured in package.json
{
"build": {
"npmRebuild": true
}
}
electron-packager does not rebuild modules by default.
electron-rebuild separately before packaging.// electron-packager: Manual rebuild step
npm run electron-rebuild
electron-packager ./app MyApp --platform=win32
pkg bakes native modules into the binary.
require statements and includes compiled .node files.// pkg: Handling native modules
// pkg automatically includes known native modules
// For dynamic paths, you must specify in package.json
{
"pkg": {
"scripts": "dist/**/*.js"
}
}
electron-builder has deep integration with update servers.
electron-updater for S3, GitHub, or generic hosts.// electron-builder: Auto-update config
{
"publish": {
"provider": "github",
"owner": "me",
"repo": "my-app"
}
}
electron-packager provides no update mechanism.
electron-updater.// electron-packager: Manual update implementation
// You must install and configure electron-updater separately
const { autoUpdater } = require('electron-updater');
autoUpdater.checkForUpdatesAndNotify();
pkg does not support auto-updates natively.
npm or brew.// pkg: No built-in updater
// Users typically update via:
npm install -g my-cli@latest
electron-builder generates platform-specific installers.
.exe (NSIS/MSI), .dmg, .deb, .rpm, etc.// electron-builder: Output example
// dist/
// MyApp Setup 1.0.0.exe
// MyApp-1.0.0.dmg
// MyApp-1.0.0.AppImage
electron-packager outputs application directories.
// electron-packager: Output example
// MyApp-win32-x64/
// MyApp.exe
// resources/
// version
pkg produces a single standalone executable.
// pkg: Output example
// dist/
// my-cli-win.exe
// my-cli-macos
// my-cli-linux
While they target different runtimes, these tools share common goals in the JavaScript ecosystem.
// electron-builder: Multi-platform build
"build": { "linux": { "target": "deb" }, "win": { "target": "portable" } }
// electron-packager: Multi-platform flag
--platform=all --arch=x64
// pkg: Multi-platform targets
"targets": [ "node18-linux-x64", "node18-win-x64", "node18-macos-x64" ]
electron-builder automates this; others require manual setup.// electron-builder: Automated signing
"win": { "sign": "./sign-script.js" }
// electron-packager: Manual signing post-build
// Use osx-sign or signtool after packaging
// pkg: Relies on external tools
// Sign the resulting binary with codesign or signtool
// electron-builder: CLI
npx electron-builder --win
// electron-packager: CLI
electron-packager . MyApp
// pkg: CLI
pkg index.js --targets node18
// electron-builder: Files config
"files": [ "**/*", "!**/.git" ]
// electron-packager: Ignore patterns
--ignore="^(src|test)$"
// pkg: Assets config
"pkg": { "assets": "public/**/*" }
// Common pattern: Webpack + Packager
// Bundle code with Webpack first, then package
// electron-builder + Webpack
"main": "dist/main.js"
// pkg + Webpack
"pkg": { "scripts": "dist/**/*.js" }
| Feature | Shared by All Three |
|---|---|
| Cross-Platform | π Win, Mac, Linux support |
| Usage | π οΈ CLI and Node.js API |
| Assets | π Configurable file inclusion |
| Signing | π Supports code signing |
| Ecosystem | π₯ Active community & plugins |
| Feature | electron-builder | electron-packager | pkg |
|---|---|---|---|
| Runtime | π₯οΈ Electron (Chromium + Node) | π₯οΈ Electron (Chromium + Node) | β‘ Node.js Only |
| Output | π¦ Installers (.exe, .dmg) | π App Directory | ποΈ Single Binary |
| Auto-Updates | β Built-in support | β Manual implementation | β External package managers |
| Native Modules | β Auto-rebuild | β οΈ Manual rebuild needed | β Baked into binary |
| Use Case | π₯οΈ Desktop GUI Apps | π₯οΈ Custom Electron Pipelines | β‘ CLI Tools / Scripts |
electron-builder is the complete solution π§° for desktop apps. It handles the messy details of signing, installers, and updates so you can focus on features. Use this for 90% of Electron projects.
electron-packager is the raw material πͺ΅ for Electron. It gives you the packaged app without the installer logic. Use this if you are building a custom distribution system or need a simpler build step for internal tools.
pkg is the specialist π§ for Node.js binaries. It skips the browser entirely to make lightweight CLI tools. Use this when you don't need a GUI and want a single file that runs anywhere.
Final Thought: Don't force a tool to do a job it wasn't designed for. If you need a desktop interface, pick an Electron tool. If you need a command-line utility, pick pkg. Within Electron, choose electron-builder for ease and electron-packager for control.
Choose electron-builder if you are building a full-featured desktop application that requires auto-updates, code signing, and support for native modules. It is the industry standard for production-ready Electron apps because it handles complex packaging scenarios and provides a unified configuration for multiple platforms.
Choose electron-packager if you need a lower-level tool that simply bundles your Electron app without extra features like auto-updates or installer generation. It is suitable for custom build pipelines where you want full control over the packaging process or if you are building a tool that wraps electron-packager itself.
Choose pkg if you are distributing a Node.js CLI tool or backend script rather than a desktop GUI application. It is the best option when you want to ship a single executable file that runs on target machines without requiring the user to install Node.js or manage dependencies manually.