Build and Package
If starter development mode works, or if your retrofitted frontend already runs through npm run app:dev, the next step is to create distributable output.
This page explains what npm run app:build does, which packaging decisions still stay configurable, and where the results appear.
1. Command
npm run app:buildThat runs:
frontron buildIn many generated starters, npm run build forwards to npm run app:build.
2. What happens during the build?
The build flow is:
- build the renderer output
- stage runtime files under
.frontron/ - package the desktop app
The runtime and packaging pipeline are still owned by frontron, not by copied template files.
3. Good checks before you build
- confirm that
npm run app:devworked at least once - save any changes to the icon, app metadata, or build policy
- make sure the terminal does not already show a runtime error
4. Common packaging decisions in frontron.config.ts
Normal packaged-app decisions stay in config:
import { defineConfig } from 'frontron'
export default defineConfig({
app: {
name: 'My App',
id: 'com.example.myapp',
description: 'Desktop build for My App',
author: 'My Team',
copyright: 'Copyright 2026 My Team',
},
build: {
outputDir: 'release',
artifactName: '${productName}-${version}-${target}.${ext}',
asar: true,
compression: 'maximum',
extraResources: ['resources'],
extraFiles: [{ from: 'licenses', to: 'licenses' }],
fileAssociations: [
{
ext: ['mydoc', 'mydocx'],
name: 'My Document',
description: 'My desktop document',
icon: 'public/icon.ico',
},
],
windows: {
targets: ['portable', 'dir'],
certificateSubjectName: 'My Team Code Signing',
requestedExecutionLevel: 'highestAvailable',
},
nsis: {
oneClick: false,
allowToChangeInstallationDirectory: true,
},
mac: {
targets: ['dmg', 'zip'],
category: 'public.app-category.developer-tools',
identity: 'Developer ID Application: My Team',
hardenedRuntime: true,
gatekeeperAssess: false,
entitlements: 'entitlements.mac.plist',
entitlementsInherit: 'entitlements.mac.inherit.plist',
},
linux: {
targets: ['AppImage', 'deb'],
category: 'Development',
packageCategory: 'devel',
},
},
})The most common fields are:
app.descriptionapp.authorapp.copyrightbuild.outputDirbuild.artifactNamebuild.asarbuild.compressionbuild.filesbuild.extraResourcesbuild.extraFilesbuild.fileAssociationsbuild.windows.targetsbuild.windows.iconbuild.windows.publisherNamebuild.windows.certificateSubjectNamebuild.windows.signAndEditExecutablebuild.windows.requestedExecutionLevelbuild.windows.artifactNamebuild.nsis.oneClickbuild.nsis.perMachinebuild.nsis.allowToChangeInstallationDirectorybuild.nsis.deleteAppDataOnUninstallbuild.nsis.installerIconbuild.nsis.uninstallerIconbuild.mac.targetsbuild.mac.iconbuild.mac.categorybuild.mac.identitybuild.mac.hardenedRuntimebuild.mac.gatekeeperAssessbuild.mac.entitlementsbuild.mac.entitlementsInheritbuild.mac.artifactNamebuild.linux.targetsbuild.linux.iconbuild.linux.categorybuild.linux.packageCategorybuild.linux.artifactNamebuild.advanced.electronBuilder
Path-based resource settings such as build.extraResources, build.extraFiles, build.windows.icon, build.nsis.installerIcon, build.mac.icon, build.mac.entitlements, build.mac.entitlementsInherit, and build.linux.icon are resolved from the project root.
build.files is different. It filters the staged packaged app contents, so keep those patterns relative to the staged app root.
build.advanced.electronBuilder is the guarded last-mile override block. Use it only for edge cases. frontron still blocks the staged app paths, package entry wiring, and the common packaging fields it already owns.
The typed signing fields describe product policy, not secret material. Certificates, signing secrets, and keychain access still come from the local machine or CI environment.
Auto updates now have a first typed config slice too:
updates.enabledupdates.providerupdates.urlupdates.checkOnLaunch
This slice currently targets packaged macOS apps with a generic feed URL. Windows auto-update is still intentionally outside the typed surface for the current packaging targets.
Deep links also have a first typed config slice:
deepLinks.enableddeepLinks.namedeepLinks.schemes
frontron stages those schemes into packaged build metadata and captures incoming URLs at runtime. Renderer code can then read them through bridge.deepLink.getState() and bridge.deepLink.consumePending().
File associations now also have a first typed config slice:
build.fileAssociations[].extbuild.fileAssociations[].namebuild.fileAssociations[].descriptionbuild.fileAssociations[].mimeTypebuild.fileAssociations[].iconbuild.fileAssociations[].rolebuild.fileAssociations[].isPackagebuild.fileAssociations[].rank
frontron maps those associations into packaged build metadata and still blocks raw fileAssociations overrides inside build.advanced.electronBuilder.
Target support is still limited by electron-builder itself. In practice, Windows associations depend on NSIS packaging and require build.nsis.perMachine: true.
5. What outputs should you expect on Windows?
With the default setup, packaged output is written under output/.
If you set build.outputDir, inspect that folder instead.
With the default Windows target setup, you will usually see:
win-unpacked/- an installer
.exe
If you change build.windows.targets, the output shape changes too.
Examples:
['portable']: portable.exe['dir']: unpacked app only['portable', 'dir']: both portable and unpacked output
The exact file names can also change with app.name, the app version, and build.artifactName.
6. What should you inspect after the build?
Start with these folders:
dist/
.frontron/
output/dist/: the built web frontend.frontron/:frontronstaging and generated filesoutput/: the default packaged desktop output
If you configured build.outputDir, replace output/ with that folder when you inspect results.
TIP
If the build succeeds but the output folders are confusing, read the next page and check each folder one by one.
WARNING
On Windows, very deep project paths can still break packaging steps. If you see file-not-found errors inside long packaging paths, try building from a shorter path such as C:\dev\my-app.