How Does Electron‑Builder Turn Your Web App into a Standalone Executable?
This article explains step‑by‑step how to package an Electron application using electron‑builder and electron‑packager, analyzes the resulting file sizes and project structure, and dives into the core source code of electron‑builder to reveal how the packaging process creates the final executable and asar archives.
How to Package an Electron Application
Electron provides a browser‑like environment with extra permissions for running web pages as desktop apps. This article explains how to package Electron apps and analyses the inner workings of electron‑builder and electron‑packager.
Packaging Tools
There are two main packaging tools for Electron:
electron‑builder electron‑packagerInstalling Dependencies for electron‑builder
yarn add electron-builder --dev
# or
npm i electron-builder --save-devPackaging with electron‑builder
Add a build field to package.json with the required metadata (name, description, version, author, etc.) and a scripts entry:
"scripts": {
"pack": "electron-builder --dir",
"dist": "electron-builder"
}Run the commands:
npm run pack # creates a directory without an installer
npm run dist # creates an installer (e.g., .exe or .dmg)Specify target platform and architecture:
# Windows 64‑bit
electron-builder --win --x64
# Windows and macOS 32‑bit
electron-builder --win --mac --ia32Installing Dependencies for electron‑packager
npm i electron-packager --save-devPackaging with electron‑packager
electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [options]The simplest way is to run electron-packager . which uses the productName or name field from package.json as the app name and defaults to the host platform and architecture.
File Size Analysis
An empty Electron project packaged with electron‑builder --dir is about 121 MB because it includes the full V8 engine and Chromium. When the installer is built, the size drops to around 36 MB, which is acceptable.
A real project with 30+ dependencies can reach 230 MB before packaging and about 56 MB after creating the installer; the increase is mainly due to the node_modules that are bundled.
Project Structure After Packaging
When using electron‑builder --dir, the output directory contains:
.
├─ locales
├─ resources
│ ├─ app.asar # contains the whole project (2 KB for an empty project, ~130 MB for a real one)
│ └─ electron.asar # Electron runtime (≈250 KB) and <code>electron.exe</code> (≈67.5 MB)
├─ electron.exe
└─ ...The app.asar archive holds the project's source files and only the production dependencies listed in package.json are included.
Analyzing electron‑builder
The packaging process can be broken down into several steps performed by the Packager class:
Normalize options from package.json.
Create a Packager instance.
Install native dependencies for the target platform.
Call packager.pack() for each platform/arch/target.
The main entry point is packages/electron‑builder/src/cli/cli.ts which exports the build function. It creates a Packager and invokes its build method.
export async function build(rawOptions?: CliOptions): Promise<Array<string>> {
const buildOptions = normalizeOptions(rawOptions || {});
const packager = new Packager(buildOptions);
// ...set up electronDownloader if needed
return _build(buildOptions, packager);
}The Packager.build() method eventually calls the private _build method, which iterates over the configured targets and architectures, installs app dependencies, creates output directories, and invokes packager.pack() for each target.
private async doBuild(outDir: string): Promise<Map<Platform, Map<string, Target>>> {
for (const [platform, archToType] of this.options.targets) {
const packager = this.createHelper(platform);
for (const [arch, targetNames] of computeArchToTargetNamesMap(archToType, packager)) {
await this.installAppDependencies(platform, arch);
const targetList = createTargets(nameToTarget, targetNames.length === 0 ? packager.defaultTarget : targetNames, outDir);
await createOutDirIfNeed(targetList, createdOutDirs);
await packager.pack(outDir, arch, targetList, taskManager);
}
}
return platformToTarget;
}Platform‑specific packagers (e.g., WinPackager) inherit from PlatformPackager. The pack method computes the application output directory, copies app files, creates app.asar, signs the app (if required), and runs any user‑defined hooks ( beforeCopyExtraFiles, afterPack, afterSign).
async pack(outDir: string, arch: Arch, targets: Array<Target>, taskManager: AsyncTaskManager) {
const appOutDir = this.computeAppOutDir(outDir, arch);
await this.doPack(outDir, appOutDir, this.platform.nodeName, arch, this.platformSpecificBuildOptions, targets);
await this.packageInDistributableFormat(appOutDir, arch, targets, taskManager);
}During doPack, the builder creates file matchers to include or exclude files, computes asar options, copies resources, runs user hooks, performs a sanity check, signs the app, and finally runs afterSign.
WinPackager Details
The Windows packager creates the electron.exe executable, modifies its icon, author, and version information, and bundles the resources/app.asar archive. The executable loads the main field from package.json inside the app.asar file.
Summary
Electron‑builder packages an app by:
Re‑installing native dependencies for the target platform.
Copying project files (filtered by matchers) into a temporary directory.
Creating app.asar (which contains the whole project) and electron.asar (the Electron runtime).
Generating the final executable ( electron.exe on Windows) and optionally signing it.
The size of the final installer is dominated by the bundled node_modules and the Electron runtime. An empty project yields a ~36 MB installer, while a real project can reach ~56 MB.
Potential issues include the fact that source files are packaged without additional minification or obfuscation, so the final binary contains the original JavaScript code.
References
Click the link in the original article to read the full source and additional documentation.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
