Getting Started
Quick Start
Section titled “Quick Start”Pick your toolchain — GJSify’s own Node-free CLI (recommended), or the same CLI through npm, Bun or Deno:
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
gjsify create my-appcd my-appgjsify installgjsify run devnpx @gjsify/cli create my-appcd my-app && npm installnpm run devbunx @gjsify/cli create my-appcd my-app && bun installbun run devdeno run -A npm:@gjsify/cli create my-appcd my-app && deno installdeno task devWhichever you pick: a GTK 4 window running your TypeScript, natively on Linux. See the Install guide for ~/.local/bin PATH setup, gjsify self-update, and gjsify uninstall.
Tip: Run
gjsify --help(ornpx @gjsify/cli --help) to see all available subcommands.
Prerequisites
Section titled “Prerequisites”You need a few system packages:
- GJS 1.86+ — the GNOME JavaScript runtime (SpiderMonkey 140)
- GTK 4 — the UI toolkit
- libsoup3 — HTTP, WebSocket and
fetchat runtime - Node.js 24+, Bun or Deno — optional, only needed if you prefer a package-manager workflow over the Node-free bootstrap above
Install commands
Fedora:
sudo dnf install gjs gtk4 libsoup3Debian/Ubuntu:
sudo apt install gjs libgtk-4-1 libsoup-3.0-0Not sure if everything is in place?
gjsify system-checknpx @gjsify/cli system-checkbunx @gjsify/cli system-checkdeno run -A npm:@gjsify/cli system-checkWhat gets scaffolded
Section titled “What gets scaffolded”gjsify create generates a minimal GTK 4 project:
my-app/├── src/│ └── index.ts # Gtk.Application entry point├── package.json # build/start/dev scripts wired to gjsify CLI└── tsconfig.jsonThe build script uses --globals auto by default — no manual list to maintain:
"scripts": { "build": "gjsify build src/index.ts --outfile dist/index.js", "start": "gjsify run dist/index.js"}You can also scaffold via
npx @gjsify/create-app my-appdirectly.
Build and run
Section titled “Build and run”The scaffolded scripts run with whichever toolchain you picked:
gjsify run build # gjsify build src/index.ts --outfile dist/index.jsgjsify run start # gjsify run dist/index.jsgjsify run dev # build + run in one stepnpm run build # gjsify build src/index.ts --outfile dist/index.jsnpm start # gjsify run dist/index.jsnpm run dev # build + run in one stepbun run build # gjsify build src/index.ts --outfile dist/index.jsbun run start # gjsify run dist/index.jsbun run dev # build + run in one stepdeno task build # gjsify build src/index.ts --outfile dist/index.jsdeno task start # gjsify run dist/index.jsdeno task dev # build + run in one stepgjsify run automatically sets LD_LIBRARY_PATH and GI_TYPELIB_PATH for any native prebuilds (e.g. @gjsify/webgl).
Notice none of the build scripts above pass --app explicitly — gjsify build defaults its target to whichever runtime is running the CLI (gjs for the Node-free bootstrap, node for the npm/Bun/Deno paths), so the same package.json script works unmodified across all four toolchains.
Using Node.js and Web APIs
Section titled “Using Node.js and Web APIs”Write standard Node.js and Web API code — the Rolldown plugin handles everything:
- Auto aliasing —
import { readFileSync } from 'node:fs'rewrites to@gjsify/fs(backed by Gio) - Auto globals —
fetch,Buffer,process,URLetc. are detected and injected automatically
import { readFileSync } from 'node:fs'import { createServer } from 'node:http'
const html = readFileSync('index.html', 'utf-8')
const server = createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/html' }) res.end(html)})
server.listen(parseInt(process.env.PORT ?? '8080'), () => { console.log('Server running on http://localhost:8080')})node:fs is backed by Gio.File, node:http by Soup.Server, process.env by GLib.getenv(). The same code works on Node.js and GJS depending on the --app target.
const response = await fetch('https://api.example.com/data')const data = await response.json()
const ws = new WebSocket('wss://echo.example.com')ws.addEventListener('message', (event) => console.log(event.data))Auto detection missed a global?
This is rare, but happens with libraries that wrap globalThis in another object (hiding the access from static analysis). Keep auto on and add the missing identifier:
gjsify build … --globals auto,matchMedia# or use a group:gjsify build … --globals auto,domSee the CLI Reference for the full list of supported identifiers.
Next steps
Section titled “Next steps”- Runtimes — target GJS, Node.js, Deno, Bun or the browser from the same source
- Install guide — Node-free bootstrap, self-update, uninstall
- Distribute your GJS app — ship a one-line installer for your own package
- Ship a GJS app as a Flatpak — submit to Flathub
- CLI Reference — all
gjsifysubcommands and flags - How It Works — auto-aliasing, prebuilds and the GJS build pipeline
- Packages Overview — 57+ Node.js, Web and DOM packages
- Contributing — help improve GJSify itself