---
title: "Code Generation"
description: "How GTKX turns GIR libraries into project-specific JavaScript bindings, JSX components, metadata, and reference documentation."
---

# Code Generation

`@gtkx/codegen` reads the [GObject Introspection](https://gi.readthedocs.io/en/latest/) (GIR) files of a project's libraries and emits executable ESM bindings, TypeScript declarations, JSX components, and reference pages. Its generated descriptors give `@gtkx/runtime` the native call signatures, so the runtime never loads libgirepository to discover them.

The public generation entry point is [`packages/codegen/src/runner.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/runner.ts). The CLI integrates it with project configuration in [`packages/cli/src/codegen/run-codegen.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/cli/src/codegen/run-codegen.ts). For command usage and library configuration, see [Configuration and Codegen](/v2/guide/configuration-and-codegen).

## Inputs and outputs

| Input                                   | What it controls                                                                              |
| --------------------------------------- | --------------------------------------------------------------------------------------------- |
| Selected GIR roots                      | Which namespaces are bound, together with everything their GIR files include.                 |
| GIR search paths                        | Which installed or project-provided GIR files supply those namespaces.                        |
| GIR annotations                         | Types, nullability, argument directions, ownership, callback scopes, and native symbol names. |
| GTKX's element configuration            | Component wrappers, additional props, omitted props, and parent-created elements.             |
| Project element configuration           | Project-specific additions and overrides to those element definitions.                        |
| Installed runtime and renderer versions | Versions recorded in the generated stores and inputs to their freshness checks.               |

Adwaita's `Adw-1` is the default root. Its dependencies include GTK4, GDK, GIO, GObject, and GLib. Configuring additional libraries extends that dependency graph. The selection logic is in [`gir/libraries.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/gir/libraries.ts).

The main outputs are:

| Output                             | Contents                                                                                                       |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `@gtkx/gi/<namespace>`             | JavaScript classes, records, interfaces, constants, functions, bootstrap code, and their `.d.ts` declarations. |
| `@gtkx/jsx/<namespace>`            | React element components and prop declarations, with references to the corresponding GI classes.               |
| Class metadata and element records | Property names, flags and defaults; signal mappings; generated element configuration.                          |
| `.gtkx/reference`                  | The project's generated element reference, when reference generation is enabled.                               |

The stores normally live under `node_modules/.gtkx/gi` and `node_modules/.gtkx/jsx`, with package links under `node_modules/@gtkx`. They are generated packages; there are no handwritten `packages/gi` or `packages/jsx` source packages to edit.

## Parsing a library graph

[`Library.load`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/gir/library.ts) starts with the requested roots and follows their GIR includes. It first discovers namespace headers and registers namespace shells, then populates their bodies and adds declarations to shared type tables. This lets the parser resolve references across namespaces and declarations that appear later in a file.

The parsed model in `src/gir` represents classes, interfaces, records, callbacks, callable parameters, properties, enums, and container types. A `TypeId` identifies a slot within a namespace's type table. Subsequent passes resolve those identities through the `Library` instead of repeatedly interpreting XML or comparing raw type strings.

[GIR annotations](https://gi.readthedocs.io/en/latest/annotations/giannotations.html) affect runtime correctness as well as documentation. An array's companion length parameter, a callback's user-data and destroy parameters, a nullable pointer, and a transfer-full return each change the descriptor the native bridge needs. Targeted annotation corrections live alongside the parser in files such as `transfer-overrides.ts` and `direction-overrides.ts`.

## Type analysis and callable shaping

The `src/analysis` modules turn the parsed model into TypeScript types and runtime descriptor expressions. Both describe the same native contract. A method's public signature may omit bookkeeping arguments while its native signature must retain them.

The callable pipeline accounts for these differences:

- Array lengths and callback bookkeeping can be derived or folded into the values exposed to JavaScript.
- Out parameters become returned values; a C return and output parameters may become a tuple.
- A trailing `GError**` becomes exception behavior while remaining an argument in the native call.
- Recognized asynchronous start/finish pairs gain promise-based wrappers.
- Records need field sizes, offsets, alignment, and ownership information in addition to their declared field types.

The mapping is implemented across [`analysis/param-structure.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/analysis/param-structure.ts), [`store/gi/param-marshal.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/gi/param-marshal.ts), [`store/gi/return-wrap.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/gi/return-wrap.ts), and [`store/gi/async.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/gi/async.ts). Descriptor construction and descriptor rendering are separate from the general TypeScript writer.

## Emitting GI bindings

[`store/gi/pipeline.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/gi/pipeline.ts) emits namespace types and members. Interfaces are emitted before the classes that implement them, and classes within a namespace are ordered so their parent declarations are available. Generated class and interface registration connects their JavaScript representations to the runtime's GType registry.

Each namespace has its main binding module and a bootstrap module. The bootstrap imports dependent namespace bootstraps, applies runtime overrides, and performs required registration or retention work. It also calls the namespace's top-level `init` function when GIR lets it be called without arguments, so `gst_init` runs before any GStreamer plugin namespace loads. Bootstraps register no library teardown; process exit releases the libraries. The public namespace barrel imports that bootstrap. The generated package marks bootstrap, override, and index modules as side effects so bundling can preserve initialization while eliminating unused exports where possible.

Overrides under [`packages/codegen/overrides`](https://github.com/gtkx-org/gtkx/tree/main/packages/codegen/overrides) are TypeScript modules compiled into the generated package. They attach runtime helpers and library-specific adaptations to generated declarations, including application lifecycle methods, non-introspectable functions, and upstream compatibility fixes. The `OVERRIDES` catalog in [`store/gi/overrides.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/gi/overrides.ts) drives module discovery, bootstrap imports, and barrel exports. The `cairo` namespace is not generated: bindings import it from the `@gtkx/cairo` package.

The runtime installs each class's interfaces as mixins, and the class declaration lists those interfaces. The signal methods (`connect`, `on`, `once`, `off`, `emit`, `disconnect`) are typed by shared runtime types; generated metadata names each class's signals.

GI methods generally defer descriptor construction and native binding through runtime factories. Runtime resolves the native symbol lazily on the first call. Importing a namespace and calling one of its methods therefore have different initialization costs; see [Native Runtime](/contributing/native-runtime).

## Emitting JSX and renderer metadata

The JSX pipeline uses the same parsed library to identify element classes and derive prop and signal types. It combines those with GTKX's built-in element configuration and the project's custom configuration.

[`packages/config/src/builtin-elements.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/config/src/builtin-elements.ts) can be imported without loading native bindings. It describes which props an element adds or omits, which component factory wraps it, and whether its parent creates the underlying object. The renderer defines attachment and update behavior in `packages/react/src/element-behaviors.ts`.

Generated element components call the renderer's `createElementComponent` factory with the GType name and class reference. Each generated class registers its property and signal metadata, so retaining the class also preserves that metadata in production bundles and application subclasses. An inherited wrapper can add application, window, or dialog lifecycle behavior.

The main sources are [`store/jsx/pipeline.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/jsx/pipeline.ts), [`store/jsx/element-components.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/jsx/element-components.ts), and [`store/gi/element-metadata.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/gi/element-metadata.ts). The [React Renderer](/contributing/react-renderer) page follows these outputs into a mounted tree.

## Store placement, freshness, and publication

Store placement follows package resolution. [`resolve-store.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/resolve-store.ts) finds the installed runtime and renderer, selects a `node_modules` location their consumers can reach, and keeps both generated packages together. Hoisted dependencies can therefore cause multiple workspace projects to share one store. The resolver rejects arrangements where a consumer sits above the generated packages and cannot import them.

A project whose store sits in another `node_modules`, such as a pnpm workspace root, gets a view in its own: real `@gtkx/gi` and `@gtkx/jsx` packages. Namespaces of the project's libraries and their GIR includes are symlinks into the shared store, so every importer loads the same files. Every other namespace exports a declaration-only stub, so importing it fails to typecheck instead of TypeScript falling back to the full store. The GI store records each namespace's includes for this in `namespaces.json`; [`store/store-views.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/store-views.ts) writes the views.

Generation acquires store locks, prepares new output in staging directories, and publishes the prepared stores and links together. Failed preparation preserves generated sources for diagnosis. The store code also restores missing links and reclaims stale staging or generation artifacts. This machinery is in [`staging.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/staging.ts) and [`store/store-fs.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/store-fs.ts).

Freshness is content-based. The GI fingerprint covers the generator, relevant dependency versions, overrides, selected roots, search paths, store version, and GIR contents. JSX has its own fingerprint for the renderer version and element configuration; regenerating GI also invalidates JSX. Documentation fingerprints include their rendering options and element configuration. These checks are implemented in [`fingerprint.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/fingerprint.ts).

The store compiler uses [Oxc](https://oxc.rs/) to emit JavaScript and isolated declarations, then removes the temporary TypeScript sources after successful emission unless `shouldKeepSources` is set. Declarations retain documentation; JavaScript retains pure annotations for tree shaking and drops other comments. A TypeScript emitter handles environments where the native transformer bindings cannot load, including Node's `--no-addons` mode. Emission diagnostics live in [`compile.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/compile.ts), with native emission in [`store/transpile.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/store/transpile.ts). Store emission does not run full semantic checking. The OpenGL generator separately checks its generated sources through `checkModules`, also with `skipLibCheck: true`.

## Reference documentation and OpenGL

Element reference pages reuse the GIR model, type rendering, and element prop configuration. [`docs/pipeline.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/src/docs/pipeline.ts) produces pages for URL-based website navigation or file-based project references. The CLI writes `.gtkx/reference` through [`codegen/reference.ts`](https://github.com/gtkx-org/gtkx/blob/main/packages/cli/src/codegen/reference.ts).

OpenGL bindings have a separate input pipeline under [`src/khronos`](https://github.com/gtkx-org/gtkx/tree/main/packages/codegen/src/khronos). It parses the bundled [Khronos OpenGL registry](https://github.com/KhronosGroup/OpenGL-Registry) (`gl.xml`), the source of the OpenGL function signatures, then selects and plans supported bindings and renders the modules used by `@gtkx/gl`.

## Verifying a change

When investigating a binding, compare the GIR signature, the generated JavaScript adapter, its `.d.ts` signature, and the runtime descriptor. A declaration-only change cannot repair a native argument that is marshalled incorrectly.

The [codegen tests](https://github.com/gtkx-org/gtkx/blob/main/packages/codegen/tests/codegen.test.ts) generate stores in temporary projects and typecheck representative valid and invalid GI and JSX consumers. Those projects use `skipLibCheck: true` and link workspace packages, so they don't cover an installed package; see [Consumer and packaging checks](/contributing/testing#consumer-and-packaging-checks). Codegen's `typecheck` target also generates bindings for the newest version of every installed GIR library and checks them without `skipLibCheck`: once as declarations, and once as the TypeScript sources the store keeps when `shouldKeepSources` is set, under the compiler options the store compiles them with.

After generation, typecheck the consuming project and exercise its native bindings.
