Packaging
How to build and package the application into a native executable and create native installer for Windows and macOS.
Overview
To build and package the application, open the project directory and run the following command in your command line:
npm run build
This command will build the application, package it into a native executable, and create a native installer for the current platform.
Third-party resources
In mobrowser.conf.json, you can set the extras property to an array of paths and wildcards that match the files and folders you want ship with your application. You can define this property for each platform individually.
The files and folders that match the paths and wildcards will be copied into the application resources directory:
- On macOS the directory is located in
<AppName>.app/Contents/Resources/. - On Windows and Linux the resources directory is located in the root directory of the packaged application.
| Entry type | Example | Behavior |
|---|---|---|
| File | path/defaults.json | Copies a single file. |
| Directory | path/assets | Copies a directory. |
| Directory content | path/assets/ | Copies content of the directory. |
| Wildcard | path/schemas/*.json | Matches files in a directory. Wildcards are allowed only in the file name segment, not in directory components (e.g. docs/*.md is valid; wildcards across path segments are not). |
{
"app": {
"bundle": {
"macOS": {
"extras": [
"config/defaults.json",
"static/assets",
"../shared/schemas/*.json"
]
}
}
}
}
Paths in each string can be a relative to the project root or an absolute path.
Custom destination & signing
If you want to put a file or a directory at a specific location inside the packaged application, you can use an object with the from, to, and an optional sign flag. For example:
{
"app": {
"bundle": {
"Windows": {
"extras": [
{
"from": "native/x64/lib.dll",
"to": "lib/x64/lib.dll",
"sign": true
},
{
"from": "images/win/",
"to": "resources/images"
}
]
}
}
}
}
Here’s the description of the properties:
| Property | Description |
|---|---|
from | Source path: a file or directory, relative to the project root or absolute. |
to | Destination path inside the packaged application. On macOS, this is relative to the Contents directory of the .app bundle. |
sign | Optional, default false. If true, the copied file or directory is code-signed. For directories, signing walks the tree: on macOS, all Mach-O binaries and code bundles under the destination are signed; on Windows, .exe, .dll and .node files. |
Native Node modules
Some npm packages cannot be bundled by Vite or rolldown — for example, packages that load native .node add-ons at runtime. To bundle these packages inside your application, add their package names to the nodeModules array under app in mobrowser.conf.json:
{
"app": {
"nodeModules": ["sqlite3"]
}
}
During build, each listed package is copied verbatim from your project’s node_modules into <app-resources>/app/node_modules next to the built JavaScript. If you code-sign the application on macOS or Windows, any unsigned .node add-ons from those packages are signed as part of that step; add-ons that are already signed are left unchanged.
Native installers
During build, a native installer for the current operating system is created. The installer is placed in the build/dist/<platform>-<arch>/pack directory.
Windows
On Windows your application is automatically packaged into a set of binary files with a native executable and a native installer.
These files are placed in the following directories:
build/
|-- dist/
| `-- win-x64/
| | `-- bin/
| | | `-- locales/
| | | `-- resources/
| | | `-- <APP_NAME>.exe
| | `-- pack/
| | | `-- <APP_SETUP_NAME>-<APP_VERSION>.exe
| | | `-- <PACKAGE_ID>-<APP_VERSION>-full.nuget
| | | |-- releases.win.jsonThe native installer in build/dist/<platform>-<arch>/pack represents a self-contained executable file that can be distributed to your users. The installer is built with Velopack.
You can customize the installer properties in mobrowser.conf.json. In the config file, there’s the app → bundle → Windows → installer → exe section where you can customize the installer name, icon, and other properties:
{
"exe": {
"name": "",
"icon": "",
"installationGif": "",
"packageId": "",
"installationFolderName": "",
"displayName": "",
"shortcuts": "",
"deltaStrategy": ""
}
}
Here’s the description of the installer properties:
| Property | Description |
|---|---|
name | The name of the installer file. If not specified, the name will be generated from the application name and version (e.g. App-1.0.0.exe). |
icon | The path to the installer icon. If not specified, the installer will have the system’s default icon. |
deltaStrategy | A strategy for generating delta packages:
|
installationGif | The path to the installation GIF. The installation GIF is displayed when the installer is running. If not specified, the user will see the default system dialog with a progress bar during installation. |
packageId | An identifier of the NuGet package by which the application is saved in the system. Used as the default folder name the application is installed under when installationFolderName is not set. If not specified, the application’s name without spaces is taken instead. |
installationFolderName | The name of the folder under %LOCALAPPDATA% where the application is installed. Unlike packageId, this value may contain whitespace and any other characters allowed by the file system, making it suitable as a human-readable installation folder name. If not specified, packageId is used as the installation folder name. |
displayName | The application name displayed in the system. Also, the same name is used for shortcuts. If not specified, the application’s name is used instead. |
shortcuts | A comma-separated list of shortcut locations.
Desktop,StartMenuRoot value is used. |
Important: Don’t forget to sign your application before distributing it. While not mandatory, code signing significantly enhances user trust and confidence in your application.
macOS
On macOS your application is automatically packaged into an .app (application bundle) and a .dmg (Apple Disk Image) file.
These files are placed in the following directories:
build/
|-- dist/
| `-- mac-arm64/
| | `-- bin/
| | | `-- <APP_NAME>.app
| | `-- pack/
| | | `-- <APP_NAME>-<APP_VERSION>-<ARCH>.dmgThe application bundle is a directory with the executable file and all the resources needed to run the application. The application bundle is a self-contained directory that can be moved around the file system without breaking the application.
The DMG file is a disk image that contains the application bundle. The DMG file is a self-contained file that can be distributed to your users. This is the recommended way to distribute your application on macOS.
In mobrowser.conf.json, there’s the app → bundle → macOS → installer → dmg section where you can customize DMG properties:
{
"dmg": {
"name": "",
"volumeName": "",
"volumeIcon": "assets/app.icns",
"eula": "",
"deltaStrategy": "BestSize",
"window": {
"textSize": 14,
"skipPrettifying": false,
"backgroundImage": "",
"position": {
"x": 500,
"y": 400
},
"size": {
"width": 600,
"height": 400
},
"icon": {
"size": 150,
"position": {
"x": 160,
"y": 160
}
},
"appDropLink": {
"x": 430,
"y": 160
}
}
}
}
Here’s the description of the DMG properties:
| Property | Description |
|---|---|
name | The name of the DMG file. If not specified, the name will be generated from the application name, version, and the current architecture (e.g. App-1.0.0-arm64.dmg). |
volumeName | The name of the volume that will be displayed in the Finder sidebar. If not specified, the name will be generated from the application name. |
volumeIcon | The path to the volume icon. If not specified, the standard volume icon will be used. |
eula | The path to the End User License Agreement (EULA) file. The text from this file will be displayed in the modal dialog that will be shown when opening DMG. In this modal dialog the user must accept or decline the license agreement. If not specified, the license agreement dialog will not be shown. |
window.textSize | The size of the text in the DMG window. |
deltaStrategy | A strategy for generating delta packages:
|
window.skipPrettifying | If true, the DMG window will not be prettified (the DMG window customization will be skipped). You might want to skip prettifying in macOS environments where there’s no logged-in user (e.g. CI build agent). |
window.backgroundImage | The path to the background image. If not specified, the DMG window will not display a background image. |
window.position | The position of the DMG window on the main screen. |
window.size | The size of the DMG window in points. |
window.icon.size | The size of the application icon in the DMG window. |
window.icon.position | The position of the application icon in the DMG window. |
window.appDropLink | The position of the application drop link in the DMG window. |
Here’s how the DMG window looks like by default:

Important: Don’t forget to sign and notarize your macOS application before distributing it. On macOS Catalina and later Gatekeeper enforces applications to be signed and notarized. Otherwise, users won’t be able to run it.