On the desktop
Real Libadwaita widgets, typed by @girs/adw-1:
Adw.SwitchRow, Adw.HeaderBar, Adw.NavigationSplitView. This is the reference the
other two ports follow, and the code runs unchanged on GJS, Node.js, Bun and Deno.
Adwaita is GNOME’s design system for GTK 4: the rows, buttons, header bars and dialogs a GNOME app is made of. GJSify gives you the whole set from TypeScript, and the same widget renders as a real Libadwaita widget on the desktop and as a custom element in the browser. It is one design system GJSify supports, not a requirement; the GTK widgets libadwaita does not ship are under Gtk.
Declare the widget tree in a Blueprint .blp file and keep TypeScript for what the widgets
do. gjsify build compiles an imported .blp to GtkBuilder XML in process, with no
blueprint-compiler installed, and xgettext finds every _("…") string in it.
Native Adwaita Apps
turns one into a window class. Reach for a UI framework only where
the tree changes shape with your data.
On the desktop the widgets come from the Libadwaita library already installed on the
machine. @girs/adw-1 is the type package for it, and nothing else is needed:
import Adw from 'gi://Adw?version=1';
const row = new Adw.SwitchRow({ title: 'Automatic updates', active: true });For the browser, install the port and import it once. That registers every element and applies the stylesheet, so from there you write HTML:
gjsify install @gjsify/adwaita-webimport '@gjsify/adwaita-web';<adw-preferences-group title="Updates"> <adw-switch-row title="Automatic updates" active></adw-switch-row></adw-preferences-group>The package entry is TypeScript, so it goes through your bundler like any other
dependency: gjsify build --app browser, the
gjsifyBrowser() Vite preset, or whatever you already use.
To change how any of it looks, set the design tokens.
| Package | What it holds |
|---|---|
@gjsify/adwaita-web | the custom elements and the compiled stylesheet |
@gjsify/adwaita-core | the behaviour both ports share, with no rendering |
@gjsify/adwaita-fonts | Adwaita Sans as TTF files plus the @font-face CSS |
@gjsify/adwaita-icons | the symbolic icons as importable SVG strings |
Every widget block is two windows. The widget runs in the first, in the browser port, so it follows the light and dark toggle. The source window below it has Blueprint, which declares the widget, and TypeScript, which builds it. Choose a segment once and every block on the site follows it.
On the desktop
Real Libadwaita widgets, typed by @girs/adw-1:
Adw.SwitchRow, Adw.HeaderBar, Adw.NavigationSplitView. This is the reference the
other two ports follow, and the code runs unchanged on GJS, Node.js, Bun and Deno.
In the browser
@gjsify/adwaita-web
gives you the same widgets as custom elements: <adw-switch-row>,
<adw-header-bar>.
On Android and iOS
@gjsify/adwaita-nativescript
reproduces the Adwaita look with real NativeScript views rather than a
webview: Adw.SwitchRow, Adw.HeaderBar. Some widgets are simplified, and
each page says where.
The web and NativeScript ports share one behaviour layer, so a fix to a toast queue or a spin-button clamp lands in both at once. For the mechanics, see How It Works.
The Adwaita Storybook is a component browser: pick a
widget, change its properties from a controls panel, and watch it update. Run it as a
native window with gjsify storybook.
Ready to assemble a whole app out of these? Start with Native Adwaita Apps.