Development Setup
Build the workspace, run an example, then use focused Nx targets while making changes. The package map identifies each package's role.
Prerequisites
Use Linux with Node.js 26.7 or later. The repository's mise.toml selects Node 26, and package.json pins pnpm through its packageManager field. If your runtimes are managed by mise, run the commands below through mise exec --, for example mise exec -- pnpm install.
Install Rust through rustup so rust-toolchain.toml selects the pinned compiler and Clippy. Native formatting and sanitizers use a separate nightly; its installation command is under Change native code.
The full workspace needs more system libraries than a minimal application:
| Work | System dependencies |
|---|---|
| Native addon and bindings | A C build toolchain, pkg-config, GObject Introspection, GLib, GTK4, and libadwaita development files. |
| Workspace library selection | GtkSourceView 5 and WebKitGTK 6.0 development files and GIR metadata. |
| Native integration fixtures | Git, Meson, Ninja, and the introspection scanner. |
| Headless examples and tests | A supported compositor, normally Sway, dbus-daemon, setpriv, Mesa rendering support, fonts, icons, MIME data, and GSettings schemas. |
| Localization and packaging checks | GNU gettext and the packaging tools used by the target formats, including RPM and Debian tooling and Flatpak helpers. |
GTKX's application baseline is GTK 4.20 and libadwaita 1.8 or later. Distribution package names and available versions vary. The CI Dockerfile records the complete verification environment, including packaging tools, fonts, and pinned toolchains.
On Debian or Ubuntu releases providing those library versions, the development packages include:
sudo apt install build-essential pkg-config gobject-introspection \
libgirepository1.0-dev libgtk-4-dev libadwaita-1-dev \
libgtksourceview-5-dev libwebkitgtk-6.0-dev meson ninja-buildThis command covers native development and fixtures, not the full headless and packaging environment listed above. Check the installed library versions before building:
pkg-config --modversion gtk4 libadwaita-1Clone and build
Clone the repository, or substitute your fork's URL:
git clone https://github.com/gtkx-org/gtkx.git
cd gtkx
pnpm install
pnpm buildpnpm install links workspace dependencies and builds the Vitest plugin's TypeScript output through the root postinstall script. pnpm build runs Nx's build targets with their dependencies. This includes the native addon, generated bindings, TypeScript libraries, application bundles, and website build where those targets exist.
The first full build is substantial: native compilation, OpenGL generation, and website API references all participate. Building references for a pinned documentation version may also need network access to retrieve that release's source and dependencies.
For subsequent work, use the target for the package you are changing:
pnpm nx run @gtkx/react:build
pnpm nx run @gtkx/react:typecheck
pnpm nx run @gtkx/react:lintNx runs the required dependency targets. A package build can therefore rebuild another package or regenerate bindings first. Inspect the graph and an individual project's targets with:
pnpm nx show projects
pnpm nx show project @gtkx/reactGenerated bindings
The root gtkx.config.ts re-exports gtkx.config.base.ts. That configuration selects the workspace's additional native libraries and reads GTKX_GIR_PATH when GIR files are installed outside the normal search paths.
Regenerate the workspace's bindings and other codegen targets through Nx:
pnpm codegenFor only the root GIR and JSX bindings:
pnpm nx run gtkx:codegenEach application example has its own configuration and codegen target. Example build and development targets generate their own bindings before starting. Generated bindings live in node_modules/.gtkx, with package links under node_modules/@gtkx; generated widget reference pages live in .gtkx/reference.
Change the generator, configuration, or source metadata when correcting generated behavior, then regenerate. Editing a generated output alone will be lost on the next run. For widget work, read the example's .gtkx/reference/index.md to find its actual element props, signals, and methods. Configuration and Codegen covers the application's view of these outputs.
Run an example
Use an example that exercises the behavior you are changing:
pnpm nx run hello-world:devThe hello-world example is a small application shell and counter. gtk-demo covers a wider set of native widgets, and storybook-example launches the native story explorer:
pnpm nx run gtk-demo:dev
pnpm nx run storybook-example:devRun these separately as needed. The development target builds dependencies, generates the example's bindings, and starts the CLI. Changes to application components participate in Fast Refresh. When editing framework packages, rebuild the affected package and restart the example as needed so it loads the changed output.
After the initial dependency build, the example's local CLI can also start a headless session:
pnpm --filter hello-world exec gtkx dev --headless --size 1280x720Keep the process's parent session alive while inspecting the app. Use the live widget tree, interactions, and screenshots to verify visible changes. The MCP guide explains how to connect, and the Storybook guide covers controls and reusable stories.
Change native code
Rust source lives in packages/native/src. Formatting and sanitizers use the nightly pinned in scripts/rust-nightly.ts. After pnpm install, install that toolchain from the repository root:
pnpm exec tsx -e '
import { execFileSync } from "node:child_process";
import { RUST_NIGHTLY } from "./scripts/rust-nightly.ts";
execFileSync("rustup", ["toolchain", "install", RUST_NIGHTLY, "--profile", "minimal", "--component", "rustfmt"], { stdio: "inherit" });
'Rebuild after changing native code, then start a fresh app or test process to load the new binary:
pnpm nx run @gtkx/native:build
pnpm nx run @gtkx/native:lint:rustThe build invokes napi build in release mode and produces the platform-specific .node file and generated addon declarations. Rust linting runs the pinned nightly rustfmt check followed by Clippy with warnings treated as errors. Changes that affect ownership, callbacks, marshalling, or teardown also belong in the native integration verification described in Testing.
Work on the website
Run the site through Nx from the repository root:
pnpm nx run @gtkx/website:devThis target generates the API references before starting VitePress. For a production build and local preview:
pnpm nx run @gtkx/website:build
pnpm nx run @gtkx/website:previewThe preview target depends on the build target. Prose changes usually belong in Markdown, while navigation, versioning, and theme behavior live in website/.vitepress. The Contributing section is shared at /contributing/ and describes repository development. Guide, Tutorial, and API Reference pages follow the prefixes declared in website/versions.json; GTKX 2's application documentation currently lives under /v2/.
API pages are generated from package output. Change the exported API documentation at its source and regenerate the reference. See Maintaining Documentation for page registration, version manifests, and promotion.
Prepare a change for review
Use Testing to select the checks that exercise your change. The broad workspace commands are:
pnpm build
pnpm test
pnpm lint
pnpm typecheckPublished-package changes use an Nx version plan created by pnpm plan. Documentation-only and test-only changes do not need one. The contribution guide covers submission and version plans; Publishing Releases is for maintainers.