Skip to content
NEWGTKX 2.0 beta is available

// React · Node.js · TypeScript · libadwaita · GTK 4

The React framework for Linux

Build native Adwaita and GTK apps with the workflow of modern web development: React components, Fast Refresh, native UI tests, and packaging, on Node.js.

Why GTKX
npm create gtkx@2.0.0-beta.14

Prefer the stable release? Use GTKX 1.6 stable.

The Notes app from the GTKX tutorial: an Adwaita window with a list of notes next to a note rendered as Markdown.The Notes app from the GTKX tutorial: an Adwaita window with a list of notes next to a note rendered as Markdown.
src/components/notes-screen.tsx
<ListView<Note>
  items={shown.map((note) => ({
    id: note.name,
    value: note,
  }))}
  renderItem={({ item }) => (
    <NoteRow note={item} />
  )}
/>
The Notes app you build in the tutorial

The workflow

From npm create to an installable package

Each step uses tools web developers already know, on real GTK and Adwaita widgets.

  1. Create

    Scaffold a project

    The scaffolder sets up an Adwaita application window, TypeScript, and tests. npm run dev opens the app.

    Terminal
    npm create gtkx@2.0.0-beta.14
    cd my-app
    npm run dev
  2. Develop

    Write native widgets in JSX

    • Adwaita and GTK widgets as components; React updates them when state changes.
    • Fast Refresh applies most edits to the open window and keeps its state.
    • Generated types for the native libraries you add, such as WebKitGTK: props, signals, and methods.
    • Node.js and npm: read files, make requests, and use packages such as Zod.
    • MCP lets your coding agent inspect and drive the running app.
    src/app.tsx
    <AdwToolbarView
      top={<AdwHeaderBar />}
    >
      <GtkScale
        adjustment={
          <GtkAdjustment upper={100} />
        }
        drawValue
        onValueChanged={(scale) =>
          setVolume(scale.getValue())
        }
      />
    </AdwToolbarView>
  3. Test

    Test the real interface

    Tests run in Vitest on a private headless display. Find widgets by role and name, click and type, and assert on what the user sees.

    tests/settings-panel.test.tsx
    it("saves the name", async () => {
      await render(<SettingsPanel />);
    
      const name = screen.getByRole(
        Gtk.AccessibleRole.TEXT_BOX,
        { name: "Display name" },
      );
      await userEvent.type(name, "Ada");
      await userEvent.click(
        screen.getByRole(
          Gtk.AccessibleRole.BUTTON,
          { name: "Save" },
        ),
      );
    
      expect(
        await screen.findByText(
          "Settings saved",
        ),
      ).toBeVisible();
    });
    npm test
    ✓ tests/settings-panel.test.tsx (1 test) 30ms
    
     Test Files  1 passed (1)
          Tests  1 passed (1)
  4. Ship

    Package for Linux

    npm run deploy builds Flatpak, Debian, RPM, and AppImage packages from your configuration, and generates the desktop entry and AppStream metadata.

    gtkx.config.ts
    export default defineConfig({
      applicationId: "dev.gtkx.Notes",
      applicationIcon: "data/icons",
      deploy: {
        name: "Notes",
        genericName: "Markdown Notes",
        categories: [
          "Utility", "TextEditor",
        ],
        targets: [
          "flatpak", "deb",
          "rpm", "appimage",
        ],
      },
    });
    npm run deploy
    .flatpakA sandboxed application with a GNOME runtime
    .debDebian, Ubuntu, and derivatives
    .rpmFedora and other RPM distributions
    .AppImageA downloadable executable file

Why GTKX

Node.js owns the process

That is why npm packages, Vite, and Vitest work in a GTKX app. GTKX targets one platform, GTK and Adwaita on Linux, and exposes all of it.

GJS

GLib main loop
SpiderMonkey: your JavaScript
no Node.js APIsno Node-native tooling
GNOME's JavaScript runtime. Your code runs inside GLib's main loop.

GTKX

Node.js event loop: your app
GLib main loop, driven by GTKX
npmViteVitestReact
GLib's main loop runs inside Node's, on the main thread.

React Native Abstracts over platforms, mostly mobile, and none of its platforms renders GTK or Adwaita.

gtk4-rs and Relm4 Rust bindings for GTK, with declarative macros in Relm4. Neither provides hot reloading, a dev server, or UI test automation.

The tutorial

Build a notes app

Build Notes one feature at a time: Markdown files, a live preview, search in a worker, and link completion. Then test it, translate it, and create installable packages.

Notes editing a Markdown note in GtkSourceView, with highlighted headings, emphasis, links, and front matter.Notes editing a Markdown note in GtkSourceView, with highlighted headings, emphasis, links, and front matter.
GtkSourceView highlights headings, emphasis, links, and front matter as you type. Read the chapter

Follow the tutorial

Open your first window

Create a project, run it, and edit it while it stays open.

npm create gtkx@2.0.0-beta.14