Skip to content

Install & Update

The CLI runs on all four runtimes, and it installs on all four. Pick the one you already have. Whichever you choose you get the same gjsify command, and the runtime you installed with becomes the one it builds for by default.

You haveInstall withUpdates with
GJSone curl line, no Node.js anywheregjsify self-update
Node.jsnpm or pnpmyour package manager
Bunbun add -gbun add -g again
Denodeno install -gthe same command with -f

On Windows take Node.js, Bun or Deno: the GJS route needs a gjs binary and there is none. On macOS all four are possible, but Node, Bun and Deno are the tested ones.

A global install is not the only shape. You can also add the CLI to a single project as a dev dependency, and for a browser or NativeScript build you may not need the CLI at all.

If you have gjs 1.86 or newer and curl, this is all you need:

Terminal
curl -fsSL https://github.com/gjsify/gjsify/releases/latest/download/install.mjs \
-o /tmp/g.mjs && gjs -m /tmp/g.mjs && rm /tmp/g.mjs

It puts the CLI and its dependencies under ~/.local/share/gjsify/global/node_modules/ and writes a small sh launcher to ~/.local/bin/gjsify. Add that directory to your PATH if it isn’t there yet:

Terminal
export PATH="$HOME/.local/bin:$PATH"

The download is checksummed before it runs. If you want the details of the bootstrap and how the CLI finds its native prebuilds, see How It Works.

Install globally
Terminal
npm install -g @gjsify/cli
Terminal
pnpm add -g @gjsify/cli

Node.js 20 or newer — the floor @gjsify/node-gi declares, and the one the CLI and its builds hold to. Pick this route if you already manage your developer tools through npm.

Yarn has no equivalent, since Yarn 2 dropped yarn global add, so use npm or pnpm for the global install even when your projects are on Yarn.

Terminal
bun add -g @gjsify/cli

The launcher lands in $BUN_INSTALL/bin (~/.bun/bin unless you moved it), which Bun’s own installer normally put on your PATH already.

Bun blocks lifecycle scripts it has not been told to trust, so the install prints something like “Blocked 2 postinstalls”. The CLI works anyway. Run bun pm -g untrusted to see which ones, and bun pm trust if you want them to run.

Terminal
deno install -g -A -n gjsify npm:@gjsify/cli

Both flags are load-bearing. -n gjsify names the executable, because Deno does not take a bin name from an npm package by itself, and -A grants the permissions the CLI needs to read your project, write build output and reach the registry.

The launcher lands in $DENO_INSTALL_ROOT/bin, or ~/.deno/bin by default. Deno prints the directory and the export PATH=… line to add if it is not on your path yet.

Nothing says the CLI has to be global. Add it as a dev dependency and it becomes one more tool in that project’s toolchain, pinned in the lockfile with everything else, so a colleague who clones the repo gets the version you built against instead of whatever they happen to have installed.

Add to a project
Terminal
npm install -D @gjsify/cli
Terminal
yarn add -D @gjsify/cli
Terminal
pnpm add -D @gjsify/cli
Terminal
bun add --dev @gjsify/cli
Terminal
deno add -D npm:@gjsify/cli

Then call it the way you call any other project-local binary. Inside a package.json script the bare name works, because the runner puts node_modules/.bin on the path:

JSON
{
"scripts": {
"build": "gjsify build src/index.ts --app gjs --outfile dist/index.gjs.js",
"start": "gjsify run dist/index.gjs.js"
}
}

From your shell, reach it through your runner: npx gjsify …, pnpm exec gjsify …, yarn gjsify …, bunx gjsify … or deno run -A npm:@gjsify/cli ….

This is how every template that gjsify create scaffolds is set up, so a project made that way already works this way and needs nothing installed globally.

For a browser or NativeScript build, no. @gjsify/vite-plugin-gjsify is an ordinary npm package whose only peer dependency is vite. Spread a preset into vite.config.ts and run plain vite / vite build: your dev server resolves modules the same way gjsify build --app browser does, so dev and production agree, and no gjsify binary is involved anywhere.

Terminal
npm install -D @gjsify/vite-plugin-gjsify vite

For a native GJS or GTK build the CLI is still the way. The Vite presets mirror the --app browser and --app nativescript targets; producing the --app gjs bundle a GNOME app ships as is the CLI’s job.

Two more plugins stand alone the same way, if all you want is one piece: @gjsify/vite-plugin-blueprint compiles .blp templates, and @gjsify/vite-plugin-gettext runs the PO/MO pipeline.

There is no single update command, because it depends on how you installed.

Update the CLI
Terminal
gjsify self-update # install the latest release
gjsify self-update --check # only tell me whether there is one
gjsify self-update --tag next # follow a different dist-tag
gjsify self-update --force # reinstall the same version
Terminal
npm install -g @gjsify/cli@latest
Terminal
bun add -g @gjsify/cli@latest
Terminal
deno install -g -A -n gjsify -f npm:@gjsify/cli

gjsify self-update only updates installs it owns, meaning the GJS bootstrap above and gjsify install -g. An npm, Bun or Deno install lands somewhere it does not manage, so it warns and stops rather than half-updating you. Use your package manager there.

On the GJS route, self-update refreshes the CLI’s runtime dependencies too (the bundler, the formatter, the native gi:// bridges). Pass --skip-deps to update only the CLI bundle, which is faster but can leave those stale.

Deno needs -f from the second install onwards: without it, it stops with “Existing installation found” rather than overwriting.

Install an exact version
Terminal
gjs -m /tmp/g.mjs --tag 0.46.0
Terminal
npm install -g @gjsify/cli@0.46.0
Terminal
bun add -g @gjsify/cli@0.46.0
Terminal
deno install -g -A -n gjsify -f npm:@gjsify/cli@0.46.0

On the GJS route --tag takes an npm dist-tag (latest, next) or an exact version, and gjsify self-update --tag 0.46.0 does the same for an install you already have.

GJS only. npm, Bun and Deno each have their own prefix setting.

Terminal
GJSIFY_GLOBAL_PREFIX=$HOME/.gjsify GJSIFY_GLOBAL_BIN_DIR=$HOME/.gjsify/bin \
gjs -m /tmp/g.mjs

GJSIFY_GLOBAL_PREFIX defaults to ~/.local/share/gjsify/global, and GJSIFY_GLOBAL_BIN_DIR to ~/.local/bin.

gjsify follows whatever runtime is running it. Installed through the GJS bootstrap it runs on GJS, so --app and --runtime default to gjs; installed from npm it runs on Node, so they default to node; installed with Bun or Deno they default to bun and deno (all three consume the same --app node bundle). Override it per command with --app / --runtime, or per project with gjsify.app in package.json.

Your operating system is never part of that decision. The CLI asks which runtime is executing it right now, nothing else (buildAppForRuntime(hostRuntime())). So on Windows it targets Node, Bun or Deno, but that is a consequence rather than a rule: it targets them because those are the only runtimes that can install and run it there. Give a machine gjs and the same CLI targets GJS, whatever the OS underneath.

gjsify showcase is the one exception: a showcase’s canonical build is its --app gjs bundle, so it picks gjs whenever gjs is installed, whatever the host.

Which version do npx, bunx and deno run give you?

Section titled “Which version do npx, bunx and deno run give you?”

Not necessarily the newest one, and the command that fails because of it will not say so. All three runners reuse a cached copy of an unpinned bin, so the same npx @gjsify/cli … can keep serving a release from months ago. Pin the tag to force a fresh resolve:

Pin the tag to force a resolve
Terminal
npx @gjsify/cli@latest showcase excalibur-jelly-jumper
Terminal
bunx @gjsify/cli@latest showcase excalibur-jelly-jumper
Terminal
deno run -A --reload --min-dep-age=0 npm:@gjsify/cli@latest showcase excalibur-jelly-jumper

Deno needs the extra flag because it applies a second rule: by default it refuses any version published in the last 24 hours and quietly falls back to an older one. That bites exactly when you have hit a bug, been told to run @latest, and got the same pre-fix binary back. --min-dep-age=0 waives it for one run, or set "minimumDependencyAge" in your deno.json.

gjsify showcase prints the version it is running as ([gjsify 0.46.0]), so check that line first when a command misbehaves.

  • A runtime: gjs 1.86+, Node.js 20+, Bun, or Deno 2.
  • gjs 1.86 or newer if you took the GJS route. Fedora 43+, Arch and Debian forky/sid ship it. Debian 13 (trixie) ships 1.82.3 and is too old: Debian went from 1.82 straight to 1.88 in forky, so no Debian stable clears the floor yet. The bootstrap checks the version and stops with install commands for the major distributions rather than failing later.
  • curl (or wget) for the GJS route.
  • An internet connection for the first run. Later installs and updates resolve from cache where they can.

Installing the CLI is not the same as being able to build a GTK app: that also needs the GNOME libraries themselves. Getting Started has the per-distribution package lists.

Remove the CLI
Terminal
gjsify uninstall -g @gjsify/cli
Terminal
npm uninstall -g @gjsify/cli
Terminal
bun remove -g @gjsify/cli
Terminal
deno uninstall -g gjsify

On the GJS route, add --dry-run to see what would go first. To remove everything by hand:

Terminal
rm -rf ~/.local/share/gjsify ~/.local/bin/gjsify

The bootstrap cache at ~/.cache/gjsify/ is safe to delete at any time; the next install rebuilds it.