Skip to content

Runtimes

GJSify’s bridge runs in both directions, so the runtime is a build-time decision, not an architecture decision:

  • On GJS, GJSify implements the Node.js and Web APIs on top of the GNOME platform — node:fs is backed by Gio, fetch by libsoup, Canvas by Cairo.
  • On Node.js, Bun and Deno, GJSify implements the GJS side: gi:// imports, GObject classes, signals and the GLib main loop, through the @gjsify/node-gi native bridge.

Either way you write standard TypeScript against standard APIs. gjsify build --app <target> picks the runtime — the same source file builds for gjs, node and browser.

The primary target. The build rewrites node:* imports and Web globals to their @gjsify/* implementations, each backed by a GNOME library. Your app runs as a single native process — GTK 4 widgets, Adwaita styling, and the npm ecosystem in one SpiderMonkey runtime.

See How It Works for the build pipeline and Packages for what is implemented.

GNOME APIs on Node.js, Bun and Deno — --app node

Section titled “GNOME APIs on Node.js, Bun and Deno — --app node”

The reverse direction. @gjsify/node-gi is a Node-API addon (vendored from node-gtk, retargeted to girepository-2.0) that resolves gi://Gtk?version=4.0 imports, GObject.registerClass, signals, virtual functions with chain-up, boxed structs, Cairo drawing and the GLib main loop — on plain Node.js. Because Node-API is also Bun’s and Deno’s native-addon ABI, one prebuilt binary serves all three runtimes.

The injection is conditional: a --app node bundle that never touches GJS APIs stays node-gi-free and runs on stock Node.js.

The devtools control plane works over this reverse bridge too: a GTK/Adwaita app running via @gjsify/node-gi on Node, Bun or Deno can be inspected, driven and screenshotted through the same org.gjsify.Devtools DBus interface as on GJS — DumpTree, GetStatus, ListToplevels and the async Screenshot all work, producing real PNGs.

The same source can target the browser. @gjsify/adwaita-web carries the Adwaita design system over as Web Components, and the bridge widgets have DOM-native counterparts — the showcases embedded on this site are exactly these builds.

gjsify itself runs on GJS, Node.js, Bun and Deno, and its defaults follow whichever one is hosting it: gjsify build defaults --app to gjs under a global GJS install and to node when run via npx/bunx/deno run (bun and deno consume the same --app node bundle). gjsify run, gjsify showcase and gjsify storybook apply the same host-derived default to their --runtime flag — so gjsify showcase canvas2d-fireworks --runtime bun picks the runtime explicitly, while omitting --runtime follows whatever runtime invoked the CLI. Override any of it with --app/--runtime or package.json#gjsify.app. Full flags: CLI Reference.

Support claims name what is actually validated, not runtime-class labels:

RuntimeRoleValidated by
GJS 1.86+Primary target, full framework10,650+ test cases run on GJS and Node.js in CI (Fedora 43/44); 35 integration suites of curated upstream tests
Node.js 24+Reverse bridge + toolchain host + CLI host261/261 node-gi engine tests; @gjsify/sqlite’s suite runs via --app node against real libgda; gjsify storybook --runtime node end-to-end; devtools (DumpTree/Screenshot/…) verified over the reverse bridge; canvas2d-fireworks showcase runs + screenshots via --runtime node; excalibur-jelly-jumper renders its full Excalibur.js game via node-gi
Bun 1.3+Reverse bridge (same binary) + CLI hostFull node-gi core parity — 215/215; devtools + canvas2d-fireworks showcase verified via --runtime bun
Deno 2.9+Reverse bridge (prebuild) + CLI hostConformance subset green — no postinstall build needed; devtools + canvas2d-fireworks showcase verified via --runtime deno
BrowserBuild target + design system12 packages tested under Playwright (Firefox/SpiderMonkey); live showcases on this site

A golden-diff conformance harness runs the same programs on gjs, node, bun and deno and requires byte-identical output; the ported GNOME GIMarshallingTests currently pass 343 cases on all four runtimes.

The runtime table above is orthogonal to the operating system. GTK/GNOME apps stay Linux-first; the cross-OS reach is specifically the node-gi (Node/Bun/Deno) path, validated per platform by what CI actually proves — again, named, not labelled with a runtime class:

Platformnode-gi (Node / Bun / Deno)GTK / Adwaita GUIPrebuilt-GTK bundle
LinuxFull — builds + display-free conformance on Node, Bun and DenoProven (GJS native and node-gi)Uses the system GTK
macOS, Apple silicon (macos-latest, arm64)Builds + display-free conformance on Node, Bun and DenoProven in CI — an Adw window realizes + renders + reacts (render-to-texture, no visible desktop)@gjsify/gtk-runtime-darwin-arm64 ships the GTK 4 / Adwaita closure (the GUI uses its --windowing variant, with libadwaita)
macOS, Intel (macos-15-intel, x64)Builds + display-free conformance on Node, Bun and DenoNot exercised on Intel — the --windowing GUI proof runs on Apple silicon only@gjsify/gtk-runtime-darwin-x64 ships the display-free GTK 4 closure; CI proves it with no Homebrew GTK on the host
Windows (windows-latest, x64)Builds (MSVC + gvsbuild) + display-free conformance on NodeGTK GUI and the full Libadwaita Storybook both render in CI (render-to-texture, no visible desktop)@gjsify/gtk-runtime-win32-x64 ships the GTK 4 / Adwaita closure (also selected by --windowing; no gvsbuild at consume time)

Node-API is the common ABI, so a single --app node prebuilt binary serves Node, Bun and Deno on a given platform. Everywhere, the runtime requirement is a C++ toolchain (or the shipped prebuild) plus GLib ≥ 2.80 / girepository-2.0 and the target library typelibs — or, on macOS and Windows, the batteries-included prebuilt-GTK bundle above.

  • Ship desktop apps on GJS. It remains the primary target — the runtime GNOME users already have installed, with the full framework surface.
  • Use Node.js, Bun or Deno where GJS isn’t available — dev tooling, CI, benchmarks, or editor integrations. gjsify storybook --runtime node is the canonical example: the same GTK storybook, running on Node.js.
  • node-gi is newer than the GJS side. It is tested and released with every GJSify release, but a breaking change may still ship in a minor version. No GJSify package depends on it at runtime, so the reverse bridge can never destabilize a GJS build — see Versioning for the stability model.

A fifth direction is taking shape: the Adwaita widget set, storybook renderer and devtools agent exist as native NativeScript components for Android and iOS (@gjsify/adwaita-nativescript — real views, not a WebView), and gjsify build --app nativescript produces bundles for the NativeScript toolchain. The runtime target itself is still experimental; the widget packages are tested and released with the regular GJSify releases.

  • How It Works — auto-aliasing, --globals auto, prebuilds
  • node-gi — the reverse bridge in depth
  • napi — the forward bridge: native Node.js .node addons in GJS
  • Coverage — live dashboards of the implemented surface
  • Versioning — release train and package tiers