Getting Started
This guide walks you through creating your first GTKX application.
Prerequisites
Before you begin, ensure you have:
- Node.js 22+ — GTKX requires a modern Node.js runtime
- GTK4 libraries — The native GTK4 runtime libraries
Create a New Project
The fastest way to start is with the GTKX CLI:
npx @gtkx/cli@latest create my-appThe CLI will prompt you for:
- Project name — lowercase letters, numbers, and hyphens
- App ID — reverse domain notation (e.g.,
com.example.myapp) - Package manager — pnpm (recommended), npm, or yarn
- Testing — whether to include Vitest testing setup
- Claude Skills — optional helper files for AI code generation
After the prompts, the CLI creates your project and installs dependencies.
Project Structure
A new GTKX project has this structure:
my-app/
├── src/
│ ├── app.tsx # Main application component
│ ├── dev.tsx # Development entry point
│ └── index.tsx # Production entry point
├── tests/
│ └── app.test.tsx # Example test
├── package.json
└── tsconfig.jsonKey Files
src/app.tsx — The default app component (just an example, can be removed or renamed):
import { useState } from "react";
import * as Gtk from "@gtkx/ffi/gtk";
import { GtkApplicationWindow, GtkBox, GtkButton, GtkLabel, quit } from "@gtkx/react";
export default function App() {
const [count, setCount] = useState(0);
return (
<GtkApplicationWindow title="My App" defaultWidth={400} defaultHeight={300} onClose={quit}>
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={20} marginTop={40} marginStart={40} marginEnd={40}>
Welcome to GTKX!
<GtkLabel label={`Count: ${count}`} />
<GtkButton label="Increment" onClicked={() => setCount((c) => c + 1)} />
</GtkBox>
</GtkApplicationWindow>
);
}src/index.tsx — Production entry point:
import { render } from "@gtkx/react";
import pkg from "../package.json" with { type: "json" };
import App from "./app.js";
render(<App />, pkg.gtkx.appId);package.json — Contains your app ID in the gtkx config:
{
"gtkx": {
"appId": "com.example.myapp"
}
}Run the Development Server
Start the development server with hot reload:
npm run devThis starts your application with Hot Module Replacement (HMR). When you edit your components, changes appear instantly without losing application state.
Build for Production
Bundle your application into a single minified file:
npm run build
npm startThis runs gtkx build to produce dist/bundle.js via Vite SSR mode, then node dist/bundle.js to run it.
Run Tests
If you enabled testing:
npm testTests run in a real GTK environment using the @gtkx/vitest plugin, which automatically manages Xvfb displays for headless execution.
Understanding the Basics
Intrinsic Elements
Intrinsic elements are imported as constants from @gtkx/react and correspond to GTK widgets or event controllers. They accept props that map to GTK properties, signals, and child widgets.
Widget Example
import { GtkButton, GtkEntry } from "@gtkx/react";
<GtkButton>Click me</GtkButton>
<GtkEntry placeholderText="Type here" />Event Controller Example
import { GtkBox, GtkLabel, GtkEventControllerMotion, GtkEventControllerKey } from "@gtkx/react";
import { useState } from "react";
const InteractiveBox = () => {
const [position, setPosition] = useState({ x: 0, y: 0 });
return (
<GtkBox focusable>
<GtkEventControllerMotion
onEnter={(x, y) => console.log("Entered at", x, y)}
onMotion={(x, y) => setPosition({ x, y })}
onLeave={() => console.log("Left")}
/>
<GtkEventControllerKey
onKeyPressed={(keyval) => {
console.log("Key:", keyval);
return false;
}}
/>
<GtkLabel label={`Position: ${Math.round(position.x)}, ${Math.round(position.y)}`} />
</GtkBox>
);
};What's Next?
- FFI Bindings — Using GTK and GLib bindings
- Styling — CSS-in-JS for GTK
- Testing — Testing your components
- Tutorial — Build a complete Notes app step by step