# electron-winstaller is ESM-only and needs Node 22.12 or newer

> 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.

**electron/windows-installer** — Build Windows Installers for Electron apps

- Repository: https://github.com/electron/windows-installer
- Stars: 1,636 · Forks: 270
- Language: TypeScript
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/electron-windows-installer

## One exported function and four output artifacts

The package has one entry point. It is ESM only, so the import is a named one:

```javascript
import { createWindowsInstaller } from 'electron-winstaller';
```

and the build call takes an options object and returns a promise:

```javascript
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:

```sh
npm install --save-dev electron-winstaller
```

## Every 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:

```javascript
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.

## 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.

## FAQ

### 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.

## Sources

- [electron/windows-installer on GitHub](https://github.com/electron/windows-installer)
- [Issues](https://github.com/electron/windows-installer/issues)
- [License: MIT](https://github.com/electron/windows-installer/blob/main/LICENSE)
- [README](https://github.com/electron/windows-installer/blob/main/README.md)
- [Releases](https://github.com/electron/windows-installer/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/electron-windows-installer
