Skip to main content
Every Zenbu.js application is itself a plugin. Your app is defined with definePlugin() the same way any third-party extension would be, and it has the same capabilities. The only thing that makes the main application special is the uiEntrypoint in defineConfig(), which tells the framework which plugin’s code to load in the renderer process first.

Defining a plugin

Plugins are declared with definePlugin() from @zenbujs/core/config:
zenbu.config.ts

Scaffolding a plugin

create-zenbu-app can scaffold a plugin folder from anywhere on disk:
The command scaffolds the plugin folder and installs its dependencies.

Adding a plugin

Plugins can be inlined with definePlugin() or referenced by path to a zenbu.plugin.ts file:
Adding or removing a plugin in zenbu.config.ts is a hot-reloadable change that takes effect without restarting the app.

Local-only plugins (localPlugins)

A zenbu.config.ts can opt into a per-developer overlay file that’s gitignored and loaded only if it exists. Use this when you want to wire a local plugin (e.g. one cloned into ~/.zenbu/plugins/...) into your app without committing the path:
zenbu.local.ts
Rules:
  • The default export is a plugin entry, or an array of entries. Same shape as plugins.
  • Relative paths inside the overlay anchor to the overlay file’s directory, not the project root.
  • localPlugins accepts a single string or an array of strings (multiple overlays).
  • If the overlay file doesn’t exist, the field is silently ignored.
  • Editing the overlay file hot-reloads exactly like editing zenbu.config.ts.
  • zen build:source / zen build:electron / zen publish:source skip localPlugins entirely, so a developer’s overlay can never ship in a build artefact.

Plugin capabilities

A plugin has the same capabilities as the host application:
  • Services run in the main process and expose methods via RPC.
  • Database schemas define sections of the shared database.
  • Events can be emitted and listened to across plugins.
  • Injections are the unified renderer-side surface. One primitive (this.inject(...) / useRegisterInjection(...)) covers React components other plugins can render via <View>, plain side-effect modules (the old “content script” pattern), plain functions/values, and advice (sugar for wrapping or replacing an existing export).

Type dependencies

If a plugin needs to call another plugin’s RPC methods, read its database, or subscribe to its events, it needs that plugin’s type definitions. You declare this with dependsOn:
zenbu.plugin.ts
name is the plugin you depend on, and from is the path to the file that defines it. With this in place, you get typed access to that plugin’s API:
This is a type-only dependency. dependsOn tells zen link where to find the other plugin’s service, schema, and event definitions so it can generate TypeScript types under <plugin>/.zenbu/types/. The generated files are import type pointers to the actual source on disk, and the entire .zenbu/types/ directory is gitignored. zen link runs automatically inside zen dev. Outside dev, run it manually before typechecking: