A Zenbu.js app ships in two pieces:Documentation Index
Fetch the complete documentation index at: https://zenbulabs.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
- The Electron app. A small
.appthat contains a launcher and a bundled package manager. This is what the user installs. - A git repository (the mirror). A separate git repository that mirrors your development repo. It contains only the files needed to run the app. On first launch the Electron app clones the mirror, and on subsequent launches it pulls the latest version.
Build config
The build is configured insidezenbu.config.ts using defineBuildConfig:
zenbu.config.ts
include and ignore
include is an array of glob patterns that determines which files from your project end up in the staged source. Everything else is excluded. Use ignore to filter out files that match an include pattern but shouldn’t ship, like tests or dev-only code.
Transforms
Build plugins let you transform files during staging or emit new files. Each plugin receives every file and can modify its contents, drop it entirely, or leave it unchanged.transform(path, contents) function that runs on each file. Returning a string replaces the file’s contents, returning null drops the file, and returning nothing leaves it as-is. The done function runs after all files are processed and can emit additional files with ctx.emit().
Git repository
Any git remote works as the source repository, for example GitHub, GitLab, or a self-hosted server. When the user launches the app for the first time, the Electron app clones this repository into~/.zenbu/<app-name>/. On subsequent launches, it fetches the latest commits and checks out the most recent compatible version. This is also how updates are delivered: push new source to the mirror, and users pick it up on their next launch.
The staged source is published to the mirror with:
Host version
The Electron app’s version number comes frompackage.json#version. zen build:electron reads it at build time and bakes it into the .app so subsequent git pulls of the source can’t change it. Bump package.json#version whenever you ship a new .app.
The source declares which Electron app versions it’s compatible with through a semver range in the same package.json:
package.json
Package manager
The Electron app bundles a package manager that runsinstall on first launch and whenever the lockfile changes. By default it bundles pnpm, but you can change it in the build config:
pnpm, npm, yarn, and bun. The specified version is downloaded and cached during zen build:electron, then packaged into the .app bundle so the user’s machine doesn’t need a package manager installed.
Installing screen
The first launch requires cloning the source and installing dependencies, which can take a few seconds. You can provide aninstalling.html file that the launcher shows during this process.
Drop it next to your renderer entry:
zen build:electron picks it up automatically. The page receives progress events through a small API exposed on window.zenbuInstall:
installing.html
| Event | Payload |
|---|---|
step | { id: "clone" | "fetch" | "install" | "handoff", label: string } |
message | { text: string } |
progress | { phase?: string, loaded?: number, total?: number, ratio?: number } |
done | { id: string } |
error | { id?: string, message: string } |
Building
There are two build steps:build:source walks your project, applies the include and ignore patterns, runs any transform plugins, and writes the staged output. build:electron bundles the launcher, the toolchain, and the installing screen into an Electron .app using electron-builder.
You can pass flags through to electron-builder:

