Skip to main content

Getting Started: JS / Electron

Applies to
Windows
MacOS
Linux

Get started with our NPM package for JS & Electron.

tip

This guide assumes you are building an Electron app with Electron Forge and the Webpack bundler, which is the recommended setup. Since Velopack contains a native binary module (.node), it works out of the box with Webpack. To scaffold a brand new project, you can use the official electron forge template:

npm init electron-app@latest my-new-app-name -- --template=webpack-typescript

Using Vite / Rollup

Velopack also works with the vite and rollup bundlers, but you must prevent them from bundling the native .node module. Externalize electron and all of your package.json dependencies so they are loaded from node_modules at runtime instead of being inlined:

// vite.config.js
import fs from 'node:fs';
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));

export default defineConfig({
build: {
rollupOptions: {
external: ['electron', ...Object.keys(pkg.dependencies || {})],
},
},
});
note

Some users have reported an invalid peer certificate: UnknownIssuer error from checkForUpdatesAsync when running under vite/rollup. This is a known issue. Do not work around it by serving updates over plain http — keep your update feed on https and ensure the system trust store is available to the packaged app.

1

Add Velopack to your package.json:

npm install velopack
2

Add the following code to your entry point (eg. index.ts) as early as possible (before any electron startup code etc.):

const { VelopackApp } = require('velopack');

// Velopack builder needs to be the first thing to run in the main process.
// In some cases, it might quit/restart the process to perform tasks.
VelopackApp.build().run();

// ... your other app startup code here
Squirrel Startup Code

If you currently have any "Squirrel" startup code you should remove this now. For example:

npm uninstall electron-squirrel-startup @electron-forge/maker-squirrel
3

This has some complexity because of electron's IPC model and that Velopack has a native node module.

First, you should setup IPC handlers in your main application thread. The updateUrl points at wherever you host your updates (a web server, S3 bucket, GitHub releases, etc.):

const { VelopackApp, UpdateManager } = require('velopack');

const updateUrl = "https://the.place/you-host/updates";

ipcMain.handle("velopack:get-version", () => {
try {
const updateManager = new UpdateManager(updateUrl);
return updateManager.getCurrentVersion();
} catch (e) {
return `Not Installed (${e})`;
}
});

ipcMain.handle("velopack:check-for-update", async () => {
const updateManager = new UpdateManager(updateUrl);
return await updateManager.checkForUpdatesAsync();
});

ipcMain.handle("velopack:download-update", async (_, updateInfo) => {
const updateManager = new UpdateManager(updateUrl);
await updateManager.downloadUpdateAsync(updateInfo);
return true;
});

ipcMain.handle("velopack:apply-update", async (_, updateInfo) => {
const updateManager = new UpdateManager(updateUrl);
await updateManager.waitExitThenApplyUpdate(updateInfo);
app.quit();
return true;
});

Unlike the C# and Python SDKs, the JS binding has no single apply-and-restart helper, so you call waitExitThenApplyUpdate (which prepares the updater and waits for this process to exit) and then exit the app yourself via app.quit().

Next, you need to expose these functions to your renderer processes in preload.ts

import { contextBridge, ipcRenderer } from "electron";
import type { UpdateInfo } from "velopack";

interface VelopackBridgeApi {
getVersion: () => Promise<string>,
checkForUpdates: () => Promise<UpdateInfo>,
downloadUpdates: (updateInfo: UpdateInfo) => Promise<boolean>,
applyUpdates: (updateInfo: UpdateInfo) => Promise<boolean>,
}

declare global {
interface Window {
velopackApi: VelopackBridgeApi;
}
}

const velopackApi: VelopackBridgeApi = {
getVersion: () => ipcRenderer.invoke("velopack:get-version"),
checkForUpdates: () => ipcRenderer.invoke("velopack:check-for-update"),
downloadUpdates: (updateInfo: UpdateInfo) => ipcRenderer.invoke("velopack:download-update", updateInfo),
applyUpdates: (updateInfo: UpdateInfo) => ipcRenderer.invoke("velopack:apply-update", updateInfo)
};

contextBridge.exposeInMainWorld("velopackApi", velopackApi);

Lastly, you can now use these functions in your renderer to provide an update UI to your users:

async function updateApp() {
// check for new version
const updateInfo = await window.velopackApi.checkForUpdates();
if (!updateInfo) {
return; // no updates available
}

// download new version
await window.velopackApi.downloadUpdates(updateInfo);

// install new version and restart app
await window.velopackApi.applyUpdates(updateInfo);
}
4

Compile your app to a binary (eg. .exe on Windows). Example using electron forge:

npx electron-forge package
5

Velopack uses a command line tool called vpk to package and publish releases. It is distributed as a .NET global tool. Although Velopack can be used with apps written in various languages, the .NET SDK is required to install and run vpk. You can install the .NET SDK from the .NET download page.

Once .NET is installed, you can install vpk by running:

dotnet tool install -g vpk
tip

It's recommended to use the same version of vpk as the Velopack package referenced in your application to ensure compatibility.

Alternatively, you can run vpk without installing it globally using the dnx command. Use the @<version> syntax to pin the version of the vpk tool itself:

dnx vpk@1.0.0

Replace 1.0.0 with the version of the Velopack package you're using in your application.

6

You are now ready to build a Velopack release for your application.

The --packId can be any unique application identifier that you wish to use. Because this must be unique across all applications, we recommend including your company name: <CompanyName>.<AppName>.

The --mainExe option is only required if your executable name is different than the --packId of your application.

See the CLI reference for more details on the available options.

vpk pack --packId YourAppId --packVersion 1.0.0 --packDir .\publish --mainExe yourMainApp.exe

✅ You're Done! Your app now has auto-updates and an installer. You can upload your release to your website, or use the vpk upload command to publish it to the destination of your choice.

You can also check out our Sample Apps to see completed examples that leverage Velopack.