electron-winstaller is ESM-only and needs Node 22.12 or newer
Build Windows Installers for Electron apps
At a glance
- What is it?
- electron-winstaller builds Windows installers for Electron apps through Squirrel.Windows. It has a narrow platform story, a long option table where most keys have sensible defaults, and one requirement that breaks older build pipelines: the package is ESM only and demands Node 22.12 or newer.
- Who is it for?
- electron-winstaller is the right choice for an Electron app that ships Windows updates through Squirrel, because it produces a .nupkg, a RELEASES file and a Setup.exe that the update path already expects. It is the wrong choice for an app that needs an MSI without Squirrel's update model, or for a build pipeline pinned to Node below 22.12, since the package is ESM only.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One exported function and four output artifacts
The package has one entry point. It is ESM only, so the import is a named one:
import { createWindowsInstaller } from 'electron-winstaller';and the build call takes an options object and returns a promise:
try {
await createWindowsInstaller({
appDirectory: '/tmp/build/my-app-64',
outputDirectory: '/tmp/build/installer64',
authors: 'My App Inc.',
exe: 'myapp.exe'
});
console.log('It worked!');
} catch (e) {
console.log(`No dice: ${e.message}`);
}Three things land in outputDirectory afterwards: an .nupkg, a RELEASES file, and an .exe installer file, one set per multi task target given under the config entry.
The Node requirement is stated twice, which is unusual and useful. The README note says the package requires Node.js 22.12 or newer, and that on 22.12 and above require() of the ESM entry point also works through Node's require(esm) support. package.json enforces the same floor with an engines field of >=22.12.0, and declares type module with exports pointing at ./lib/index.js. Installing it is one command:
npm install --save-dev electron-winstallerEvery metadata option defaults to a package.json field
The configuration table is long, but most of it follows one pattern: a NuGet metadata value that falls back to a field in your app's package.json when you do not supply it.
authors falls back to the author field, owners to authors, description to description, version to version. title is more layered, defaulting to productName and then to name. exe is the main executable name, derived from name with an .exe extension. name is the Windows Application Model ID, the appId, defaulting to the package name.
Only two entries in the whole table are marked required: appDirectory, the folder path of your Electron app, and authors, which is required in the table even though it has a documented default.
outputDirectory defaults to an installer folder at the project root, which is the setting most worth making explicit in a CI script, since a silent write into the repository root is easy to miss in a log.
The visual options are separate: loadingGif names a local .gif shown during install, iconUrl points at an ICO used as the application icon in Control Panel, and setupIcon sets the icon for the generated Setup.exe. iconUrl defaults to the Atom icon, which you will almost certainly change.
Code signing has two paths, and one of them overrides the other
The README section heading about signing is blunt: sign your installer or else bad things will happen. Unsigned installers are fine for development and internal use, but in production Internet Explorer's SmartScreen filter will block the download, and many anti-virus vendors will treat the application as malware.
The simple path is a certificate file plus its password:
The certificate path goes in certificateFile and the passphrase in certificatePassword.
Any certificate valid for Authenticode Code Signing works. The README adds that a Standard Code Signing certificate is sufficient if you also want to opt into Windows Error Reporting.
There is a third option, signWithParams, which takes parameters passed to signtool and, as the table states, overrides certificateFile and certificatePassword. That precedence is the detail to remember: if both are present, the certificate path is ignored.
For anything beyond that, windowsSign switches to @electron/windows-sign for advanced code signing, and that package is a runtime dependency of this one at ^2.1.0, so it is already installed when you need it.
fixUpPaths, skipUpdateIcon and other workarounds with a story behind them
Several options exist because of specific problems, and reading them tells you what the maintainers hit in the field.
fixUpPaths renames the generated Setup.exe and Setup.msi to more descriptive names based on setupExe, setupMsi or the app's productName, for example MyAppSetup.exe. It is enabled by default and you set it to false to keep the original Setup.exe and Setup.msi names.
skipUpdateIcon disables setting the icon of Update.exe. The README names the failure it solves: an installation error reading "This application could not be started" when the setup is built on a non-Windows system. That is a cross-compilation artefact, and the fix is to stop stamping the update binary at all.
noMsi controls whether Squirrel.Windows creates an MSI installer, and noDelta controls delta packages, where the README's advice is to disable them only if necessary because they are a Good Thing. For delta updates to work you point remoteReleases at the URL of your existing updates, and remoteToken supplies the authentication token for that fetch.
frameworkVersion sets the required .NET framework version, for example net461. It appears only in this table, with no further explanation anywhere in the visible README, so anyone targeting an old Windows baseline has to test it directly.
Cross-building works through Wine and Mono, but not on 32-bit machines
Installers can be created on x64 and arm64 build machines, which the README defines as Windows, or macOS and Linux with Wine and Mono. Thirty-two bit build machines are not supported, and if you are on 64-bit Windows the instruction is to make sure you are running 64-bit Node.js.
The target and the build machine are separate questions, which is the useful part. Apps targeting 32-bit Windows, meaning Electron 43 and earlier, can still be packaged from a 64-bit machine, and the generated installer runs on 32-bit Windows. Electron 44 and later do not ship 32-bit Windows builds at all, so that combination stops being a build matrix question and becomes a support question.
The Wine and Mono route is how a macOS or Linux CI runner produces a Windows installer without a Windows agent. The skipUpdateIcon option exists for exactly that setup, so if you build that way, set it and the icon problem documented above does not arise.
Your app has about fifteen seconds to handle a Squirrel event
The last piece of the contract is runtime behaviour, and it is the part most likely to cost you an afternoon. Squirrel spawns your app with command line flags on first run, on updates, and on uninstalls. The README is emphatic that the app must handle these events as early as possible and quit immediately after handling them, because Squirrel gives the app roughly fifteen seconds to apply the operations and quit.
So a slow startup, a splash screen that waits on the network, or an update check before the event handler runs will break updates in a way that looks like a corrupted install.
The common events, including desktop shortcut management, are handled by the electron-squirrel-startup module, and the fix is one line at the top of main.js:
if (require('electron-squirrel-startup')) return;Inside an ESM-only project that require call is itself worth checking against your bundler, since this README line predates the ESM only constraint documented above.
On the build side, the repository uses oxlint for linting, TypeScript for the build step and typedoc for API documentation, with tests run through the Node test runner via jiti register and a sixty second timeout per spec. published with provenance enabled.
Editorial conclusion
electron-winstaller is the right choice for an Electron app that ships Windows updates through Squirrel, because it produces a .nupkg, a RELEASES file and a Setup.exe that the update path already expects. It is the wrong choice for an app that needs an MSI without Squirrel's update model, or for a build pipeline pinned to Node below 22.12, since the package is ESM only. Before adopting, decide how you will sign: certificateFile for a plain Authenticode certificate, or the windowsSign option for anything more involved, because unsigned installers are blocked by SmartScreen.
Frequently asked questions
How do I install electron-winstaller and what Node version does it need?
Run `npm install --save-dev electron-winstaller`. The package is ESM only and requires Node.js 22.12 or newer, enforced by an engines field of >=22.12.0 in package.json.
What files does createWindowsInstaller produce?
It creates an .nupkg, a RELEASES file and an .exe installer file in the outputDirectory folder, one set for each multi task target given under the config entry.
How do I code sign an Electron Windows installer?
Set certificateFile to an Authenticode Code Signing Certificate and certificatePassword to its password, or use signWithParams to pass signtool parameters, which overrides those two options. For advanced signing, set windowsSign to use @electron/windows-sign.
Can I build a Windows installer on macOS or Linux?
Yes, on x64 or arm64 machines with Wine and Mono. 32-bit build machines are not supported, and skipUpdateIcon is provided to avoid an installation error when the setup is built on a non-Windows system.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/electron-windows-installer)Was this useful?