writing gjs gtk app in typescript

06 Jan 2025

Recently I wanted to write a small GTK utility for personal use, and I was looking into writing it in TypeScript. The Gnome Builder bootstraps in JavaScript, so I was exploring the ways which I can set up the project without Gnome builder.

In case you want to skip all the manual steps, and want to bootstrap the application (including packaging) in single command you can skip to Bootstrap using create-gtk after installing the system dependencies.

Install dependencies

I am using Debian 12 (bookworm) I installed the GTK and other system dependencies using apt-get.

sudo apt-get install libgtk-4-dev pkg-config meson gjs libadwaita-1-dev

libadwaita-1-dev is required only if you are planning to use Adwaita widgets.

Setup typescript project

As usual, I start to set up the project by

mkdir example-gtk
cd example-gtk
npm init -y

and install the NPM dependencies like

npm i -D typescript esbuild

In order to set up the types of gjs, we can use

npm i -D @girs/gtk-4.0 @girs/adw-1 @girs/gjs

If you prefer to generate the types, you can use generate command from NPM package @ts-for-gir/cli

The tsconfig.json will look like

{
  "compilerOptions": {
    "types": ["@girs/gjs", "@girs/adw-1"],
    "lib": ["ES2024", "DOM"],
    "target": "ES2024",
    "module": "NodeNext",
    "moduleResolution": "node16",
    "resolveJsonModule": true,
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
    "alwaysStrict": true
  },
  "include": ["src"]
}

Now we can set up an entry point for our application.

// src/index.ts

import Gio from "gi://Gio";
import Adw from "gi://Adw?version=1";
import GObject from "gi://GObject";
import Gtk from "gi://Gtk";

class _Application extends Adw.Application {
  constructor(
    constructProperties = {
      application_id: "com.<user>.<project.name>",
      flags: Gio.ApplicationFlags.FLAGS_NONE,
    }
  ) {
    super(constructProperties);

    const quit_action = new Gio.SimpleAction({ name: "quit" });
    quit_action.connect("activate", (action) => {
      this.quit();
    });
    this.add_action(quit_action);
    this.set_accels_for_action("app.quit", ["<primary>q"]);
  }

  override vfunc_activate() {
    super.vfunc_activate();
    let win = this.active_window;
    if (!win) {
      win = new Gtk.Window({
        title: "<project.name>",
        default_width: 800,
        default_height: 800,
        application: this,
      });
    }

    win.present();
  }
}

/** Main Application class */
const Application = GObject.registerClass(
  {
    GTypeName: "Application",
  },
  _Application
);

/** Run the main application */
const main = () => {
  const app = new Application();
  app.run([imports.system.programInvocationName].concat(ARGV));
};
main();

Setup build

I use esbuild to build the typescript application. The basic configuration is given below. The target can be set based on the gjs version, a reference is given along with the esbuild configuration. Support for sourcemap has recently merged with Gnome 48, but since it is not available to me, I will skip this option.

// esbuild.js

import { build } from "esbuild";

await build({
  entryPoints: ["src/index.ts"],
  outdir: "dist/",
  bundle: true,
  // target: "firefox60", // Since GJS 1.53.90
  // target: "firefox68", // Since GJS 1.63.90
  // target: "firefox78", // Since GJS 1.65.90
  // target: "firefox91", // Since GJS 1.71.1
  // target: "firefox102", // Since GJS 1.73.2
  target: "firefox115", // Since GJS 1.77.2
  format: "esm",
  external: ["gi://*"],
});


Now we can run node esbuild.js to build our application.

If the build is successful, we can use the following command to check that the application is running without errors.


gjs -m "dist/index.js"

We can enable debug logging using the ENV variable, G_MESSAGES_DEBUG.

G_MESSAGES_DEBUG=all gjs -m "dist/index.js"

basic GTK window in gjs

Bootstrap using create-gtk

In order to avoid doing all the above steps manually, I wrote a NPM initializer create-gtk to bootstrap gjs GTK4 apps with some defaults.

You can use it by running the command

npx create-gtk <project-name>

This will initialize the gjs GTK4 project with TypeScript & esbuild.

Now we can run the following commands to build

npm i
npm run build
chmod +x bin/<project-name>

and test run the newly bootstrapped application using

./bin/<project-name>

The bootstrapped application will use Adwaitda widgets, and will have a title bar, menubar, about window & sample code for toast messages.
Packaging instructions will be available in the generated README.

The create-gtk code is available at https://github.com/revathskumar/create-gtk.

Helpful Links & Honourable Mentions

  • https://gjs.guide/guides/
  • https://rmnvgr.gitlab.io/gtk4-gjs-book/
  • https://github.com/gjsify/example-gtk4 by Pascal Garber.
  • https://github.com/gjsify/ts-for-gir/
  • https://gjsify.org/pages/projects

Hope this is helpful.

Versions of Language/packages used in this post.

Library/Language Version
Debian 12 (bookworm)
Gtk 4
libgtk-4-dev 4.8.3
meson 1.0.1
gjs 1.74.2
libadwaita-1-dev 1.2.2-1
TypeScript 5.7.2
If you find my work helpful, You can buy me a coffee.