# gjsify: Running npm Packages Natively on GNOME Without Node.js

> gjsify reimplements Node.js, Web, and DOM APIs on top of GNOME's Gio, Soup, Cairo, and GStreamer, letting unmodified npm packages run as native GTK apps. It targets GJS 1.86+, ships a Node-free CLI, and has clear limits for non-GNOME runtimes.

**gjsify/gjsify** — The full JavaScript ecosystem, native on GNOME

- Repository: https://github.com/gjsify/gjsify
- Website: https://gjsify.github.io/gjsify/
- Stars: 24 · Forks: 4
- Language: TypeScript
- License: MIT
- Published: 2026-08-22 · Updated: 2026-08-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/gjsify-gjsify

## The Gap gjsify Fills: GJS Is Not Node or a Browser

The primary audience is a developer who already knows JavaScript and wants to build a native GNOME desktop app without learning a separate language or giving up the npm packages they already use. The README cites real proof: Excalibur.js, WebTorrent, socket.io, three.js, axios, and the Anthropic MCP SDK all run on GJS through gjsify's polyfills, validated by their own upstream test suites. That is a strong claim, and it is the core value proposition. If you are not building for GNOME, or if you are happy with Electron or a web app, gjsify has no obvious advantage.

## How the Polyfills and Bundler Work Together

The mechanism is a combination of TypeScript polyfills and a build-time import resolver. Most polyfills are pure TypeScript, which makes them portable across runtimes. Each package declares its reach across GJS, Node, Browser, and NativeScript. When you target GJS, the bundler resolves `node:*` imports to their `@gjsify/*` implementations. The README shows a simple example: importing `readFileSync` and `writeFileSync` from `node:fs` and `createHash` from `node:crypto`, then using them as if they were Node. The bundler routes each import to the right implementation based on the target runtime. That is the one-source-many-runtimes idea: share what is shareable, be native where it counts. The `fetch` example goes out over `libsoup`, not a simulated network stack. The bridges are particularly interesting for browser-shaped libraries. A `<canvas>` becomes a `Gtk.DrawingArea` for 2D or a `Gtk.GLArea` for WebGL. An `<iframe>` becomes a `WebKit.WebView`. That means a game engine written for the browser can render inside an Adwaita window without modification. The README says "unchanged," which is the boldest promise in the project. The build system emits a GJS bundle and a Node/Bun/Deno bundle from the same source file, so you can target multiple runtimes from one codebase. The trade-off is that the bundler must know about every API you use; if a polyfill is missing, the build will fail or the runtime will throw.

## Getting Started: The Node-Free CLI and Project Scaffolding

The installation is deliberately Node-free. You need only `gjs` 1.86 or newer and `curl`. The bootstrap command downloads an installer script and runs it with GJS: `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`. That installs `@gjsify/cli` under `~/.local/share/gjsify/global/` and puts a launcher at `~/.local/bin/gjsify`. You can also install via npm with `npm install -g @gjsify/cli` if you already manage CLIs that way. To create a project, run `gjsify create my-app --template gtk-minimal`, then `cd my-app`, `gjsify install` to resolve dependencies, `gjsify run build` to build, and `gjsify run start` to launch the GJS bundle. The `create` command scaffolds a GTK 4 + TypeScript app with build scripts included. The `run build` command emits both a GJS bundle and a Node/Bun/Deno bundle from the same `src/index.ts`. For development, `gjsify run dev` builds only the GJS half and launches it. The CLI also has direct commands: `gjsify build src/index.ts --app gjs --outfile dist/app.js`, `gjsify run dist/app.js`, and `gjsify dlx <pkg>` to try a published app without installing. One detail worth noting: the `--app` flag has no fixed default. It follows the runtime the CLI itself is running on. If you bootstrap via GJS, omitting `--app` gives a GJS bundle. If you installed via npm, omitting it gives a Node bundle. That is a potential footgun if you switch between the two installation methods.

## Dependencies and System Requirements: GJS 1.86 and GNOME Dev Libraries

The runtime requirement is GJS 1.86 or newer, which corresponds to SpiderMonkey 140 and ES2024. The README says that ships with Fedora 43+ and Ubuntu 25.10+. That is a recent requirement; older distributions will not work. You also need the GNOME development libraries for the features you use. The README lists Fedora and Ubuntu packages. For Fedora: `gjs glib2-devel gobject-introspection-devel gtk4-devel libsoup3-devel webkitgtk6.0-devel libadwaita-devel gdk-pixbuf2-devel libepoxy-devel libgda libgda-sqlite meson vala gcc pkgconf`. For Ubuntu: `gjs libglib2.0-dev libgirepository1.0-dev libgtk-4-dev libsoup-3.0-dev libwebkitgtk-6.0-dev libadwaita-1-dev libgdk-pixbuf-2.0-dev libepoxy-dev libgda-6.0-dev meson valac gcc pkg-config`. The list is long, and it is not optional if you want the full API surface. The README says Node.js 24+ is optional, needed only for the cross-validation test track or if you manage the CLI via npm. That means you can develop and ship a GNOME JavaScript app on a machine with no Node.js at all, which is a distinctive claim. The packaging story is also Node-free: `gjsify ship` produces a `.deb` and an `.rpm` without requiring `dpkg-deb` or `rpmbuild` on the machine, and `gjsify flatpak init` generates the full Flathub asset set. Those commands are Linux-only and `--app gjs` only, per the README.

## Limitations: What gjsify Is Not and Where It Can Break

The most obvious limitation is that gjsify only targets GNOME on Linux. The README states GJS is the primary, non-negotiable target. There is no mention of Windows or macOS support, and GJS itself is a GNOME runtime. If your users are on other platforms, you cannot ship a gjsify app to them. The second limitation is the dependency on a very recent GJS version. GJS 1.86 is not available on every Linux distribution; Fedora 43+ and Ubuntu 25.10+ are named, but many LTS distributions will be behind. That could force you to upgrade your base system or use Flatpak with a newer runtime. The third limitation is the completeness of the polyfills. The README lists many APIs, but it does not claim full coverage. The success metric is "unmodified npm packages running on GJS," but that is validated only for the packages named. A package that uses an obscure Node.js API or a Web API not yet implemented will fail. The README does not provide a compatibility matrix, so you cannot know in advance without running the upstream test suite. The fourth limitation is the build complexity. The bundler must resolve every import correctly for the GJS target. If you use dynamic imports or conditional requires that the bundler cannot statically analyze, you may hit edge cases. The README does not discuss these failure modes, so you should expect to debug them.

## Alternatives: Electron, Node.js with GObject Bindings, or Plain GJS

The obvious alternative is Electron, which bundles Chromium and Node.js to give you the full web platform on any desktop OS. The difference is fundamental: Electron ships a large runtime, uses more memory, and produces a heavier app. gjsify produces a native GTK app that uses the system's GJS and libraries, which is lighter and more integrated with the GNOME desktop. The trade-off is that Electron supports Windows and macOS, while gjsify does not. Another alternative is to use GJS directly with GObject bindings, writing your app in JavaScript but without the Node.js and Web APIs. That is what gjsify builds on, but it means you cannot reuse most npm packages. You would have to use `Gio` for file I/O and `Soup` for HTTP yourself, which is more verbose and less familiar to most JavaScript developers. A third alternative is to run Node.js as a sidecar process and communicate with your GTK app via IPC. That gives you the full Node ecosystem, but it adds a second runtime and a communication layer, which is exactly what gjsify avoids. The README explicitly says no shelling out to Node. The choice depends on whether you prioritize native integration and small footprint or cross-platform reach and complete Node compatibility.

## Maintenance and Licensing: What to Check Before Committing

The repository metadata shows an unknown license, which is a red flag for adoption. The README does not mention a license identifier. Before using gjsify in a commercial project, you must clarify the license with the maintainers or check the repository files directly. The project is active, with recent releases and a last push in August 2026, but activity alone does not guarantee long-term maintenance. The README references a project status page and an architecture guide (`AGENTS.md`), which suggests the project has a contributor structure, but the material does not detail release cadence or backwards compatibility guarantees. The CLI has a `self-update` command and a lockfile mechanism (`gjsify-lock.json`) for reproducible installs, which helps with upgrades. The `install --immutable` flag fails when the lockfile drifts, so CI can catch dependency changes. That is a practical maintenance feature. However, because the polyfills are implemented in TypeScript and the bundler routes imports, upgrading gjsify may change how a given API behaves if the underlying GNOME library version changes. You should pin your GJS version and test your app after each gjsify update. The cross-validation test track on Node and GJS is a positive sign for catching regressions, but it is optional and requires Node.js 24+, so not every contributor will run it.

## Conclusion

Adopt gjsify if you are building a GTK 4/Adwaita app for GNOME and want to reuse existing npm packages like axios, three.js, or WebTorrent without a Node.js runtime. It is the right tool when you need a native Linux app that speaks the JavaScript ecosystem's language. Do not use it if your target is Windows, macOS, or a non-GNOME Linux desktop, since GJS is the primary and non-negotiable target. Also avoid it if your app depends on Node.js-specific internals that gjsify has not implemented, such as full cluster or child_process semantics; the README lists many APIs but does not claim complete coverage. Before adopting, verify that every npm package you depend on has its tests passing on GJS via gjsify's polyfills, and check the project status page for known gaps. The CLI requires GJS 1.86 and SpiderMonkey 140, so confirm your distribution ships that version; Fedora 43+ and Ubuntu 25.10+ are mentioned. Start with a small proof-of-concept using `gjsify create my-app --template gtk-minimal` and run the upstream test suite of your key dependency to see if it truly works. The project's own success metric is unmodified npm packages passing their own tests, so that is the concrete gate you should apply.

## FAQ

### Do I need Node.js installed to use gjsify?

No. The bootstrap installs the CLI with gjs 1.86 or newer and curl alone, laying `@gjsify/cli` under `~/.local/share/gjsify/global/` with a launcher at `~/.local/bin/gjsify`. Node 24 or newer is optional, needed only for the cross-validation test track or for installing the CLI through npm.

### Which npm packages are known to run on GJS through gjsify?

The Excalibur.js game engine, WebTorrent, socket.io, three.js, axios and the Anthropic MCP SDK are named as running on GJS through the polyfills, validated by their own upstream test suites rather than by a project-specific demo.

### Why do I need to pass --app gjs to gjsify build?

Because `--app` has no fixed default and follows the runtime the CLI itself is running on, `gjs` for a bootstrapped install and `node` for an npm-installed one. Omitting it on a Node-installed CLI gives a Node bundle rather than a GJS one, with no error to indicate the difference.

### What does gjsify map node:sqlite onto?

libgda, alongside `Gio` and `GLib` for `node:fs`, `node:net` and `node:crypto`, Soup 3 for `fetch`, `WebSocket` and `XMLHttpRequest`, Cairo and `Gtk.GLArea` for `<canvas>`, and GStreamer for `WebRTC`, `WebAudio` and `<video>`.

## Sources

- [Official documentation](https://gjsify.github.io/gjsify/)
- [Official README](https://github.com/gjsify/gjsify#readme)
- [Project repository](https://github.com/gjsify/gjsify)
- [Release notes](https://github.com/gjsify/gjsify/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gjsify-gjsify
