Overview
gjsify is a TypeScript framework for native desktop apps. You write TypeScript
and you get a real GTK 4 / Adwaita application: one native process, no bundled
browser engine, no second process behind the window. GTK and libadwaita stay
exactly as they are, reached through gi:// imports.
Four JavaScript runtimes can run that app: GJS, Node.js, Bun and Deno. What
differs between them is where gi:// comes from. GJS is GNOME’s own JavaScript
runtime, so it resolves gi:// itself with nothing in between. Linux
distributions ship it and Homebrew builds it for macOS, but there is no GJS for
Windows. On Node, Bun and Deno the same imports resolve through
@gjsify/node-gi, a Node-API addon, and that is the
route reaching all three operating systems.
The code you write is the same either way. Where a runtime has no implementation
of its own, gjsify supplies one: on GJS that means node:fs, fetch, WebSocket
and <canvas>, backed by Gio, libsoup and Cairo, which is what lets a large slice
of npm run there. Where the runtime already has the module, you get the runtime’s
own.
What you can build. A desktop app with the full Adwaita widget set. A game
rendering with WebGL into a real Gtk.GLArea or with Canvas 2D into a
Gtk.DrawingArea, where three.js and Excalibur.js run unmodified. A command-line
tool. An HTTP server on Express or Hono. gjsify create scaffolds a starter for
each of those, and a bare Gtk.Window if you want to begin from nothing. Every
one of them builds for all four runtimes, and the same source builds for the
browser too.
The CLI runs on all four runtimes as well, so you develop with the one you already have.
Start here: Getting Started takes you from nothing to a window on screen.
Choose the target when you build
Section titled “Choose the target when you build”Where your code runs is a flag on the build, not a decision baked into your app:
gjsify build --app | Runs on | What that gives you |
|---|---|---|
gjs | GJS, on Linux; macOS in part | GNOME’s own interpreter resolves gi:// directly, with nothing bridging into GTK. Linux distributions ship the runtime. On macOS it comes from Homebrew and only part of it is verified there, and there is no gjs for Windows. |
node | Node.js, Bun and Deno, on Linux, macOS and Windows | One bundle for all three, because Node-API is their common ABI. gi:// goes through @gjsify/node-gi, and @gjsify/gtk-runtime-darwin-arm64, -darwin-x64 and -win32-x64 carry GTK itself, so those two platforms need no system GTK. |
browser | any modern browser | No native code at all, so it is portable by construction. Adwaita comes over as Web Components through @gjsify/adwaita-web. |
nativescript | Android and iOS, experimental | Real native views, no webview. @gjsify/adwaita-nativescript carries the widget set. Try it; do not ship on it yet. See Runtimes. |
The flag defaults to whatever runtime is running the CLI, so a CLI installed
through the GJS bootstrap builds gjs bundles and one installed with npm, Bun or
Deno builds node ones. Override it per command with --app, or per project with
gjsify.app in package.json. Runtimes covers what each
target gives you; Platform Support answers the
operating-system question.
What makes GJSify different
Three worlds, one runtime
GNOME, Node.js, and the Web have lived in separate runtimes for a decade. GJSify dissolves the boundary — every API lands inside one SpiderMonkey process and shares the same memory, the same event loop, the same GTK main context.
- Gtk 4 widgets
- libadwaita
- GIO file / network / DBus
- GStreamer pipelines
- WebKit, libsoup, Cairo
fs·net·http(s)stream·events·processcrypto·worker_threadsutil·buffer·url- 44 of the 50 stdlib modules
fetch·WebSocket· SSECanvas 2D· WebGL · WebGPU bridge- WebRTC · WebAudio · Gamepad
FormData· Streams · Crypto- DOM events, Mutation / Resize observers
One SpiderMonkey process. One TypeScript codebase. Zero glue between worlds.
For example
Ship a browser game engine — without shipping a browser.
Excalibur.js renders directly into a Gtk.GLArea through GJSify's WebGL bridge.
Pointer, keyboard, and gamepad input flows through GTK4 event controllers, dispatched as W3C DOM events.
Audio runs on GStreamer through the Web Audio API. The whole game lives in one process — the same
one that draws the Adwaita window around it. No WebKit.WebView, no IPC bridge, no
embedded Chromium.
Where to go next
Section titled “Where to go next”- Getting Started: scaffold a project, get a window on screen
- Widget Gallery: every Adwaita component, with live previews and code
- UI Frameworks: render a GTK window from Solid, Vue or React, or bring React Native code to it
- Native Adwaita Apps: the app shell, navigation, dialogs and toasts
- Runtimes: GJS, Node.js, Bun, Deno, the browser and mobile, and what runs where
- Packages: the Node.js, Web and DOM APIs gjsify implements
- Showcases: full apps that run natively and in your browser from one source
- CLI Reference: every
gjsifycommand and flag - How It Works: the build pipeline, if you want to look inside