Skip to content

Presentation

These widgets show state and identity rather than collect input: an avatar for a person or account, a banner for an in-context message, a shortcut label to draw a keyboard shortcut as keycaps, a spinner while work runs, a status page for an empty or error state, and a window title in a header bar.

What the preview does not show

The previews run the browser port, so behaviour Libadwaita drives natively (a spinner’s animation timing, a banner’s reveal transition) can be simplified here. The NativeScript port diverges in two places worth knowing: its avatar has no icon fallback, and its banner takes plain text rather than markup.

Stand in for a person, account or entity. Ask for initials and give it a name, and it derives the initials and a stable background colour from that name; with either missing it falls back to a symbolic icon. Size scales it from an inline list glyph up to a full profile header.

Adw.Avatar
Blueprint
Code
using Gtk 4.0;
using Adw 1;
Adw.Avatar avatar {
text: "Ada Lovelace";
size: 96;
show-initials: true;
icon-name: "avatar-default-symbolic";
}
TypeScript
TypeScript
import { build } from './avatar.blp';
const { avatar } = build();
avatar.text = 'Grace Hopper';
Web Components
index.html
HTML
<adw-avatar
id="avatar"
text="Ada Lovelace"
size="96"
show-initials
icon-name="avatar-default-symbolic"
></adw-avatar>
<script type="module">
import '@gjsify/adwaita-web';
const avatar = document.querySelector('#avatar');
avatar.setAttribute('text', 'Grace Hopper');
</script>
main.js (Blueprint)
JavaScript
import { mountSharedTree } from '@gjsify/adwaita-web';
import tree from './avatar.blp?shared-tree';
mountSharedTree(tree);
const avatar = document.querySelector('#avatar');
avatar.setAttribute('text', 'Grace Hopper');
NativeScript
views/avatar.xml
XML
<adw:Avatar
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adw"
id="avatar"
text="Ada Lovelace"
size="96"
showInitials="true"
iconName="avatar-default-symbolic"
/>
app.ts
TypeScript
import { type Adw } from '@gjsify/adwaita-nativescript';
import { Builder } from '@nativescript/core';
const avatar = Builder.load({ path: '~/views', name: 'avatar' }) as Adw.Avatar;
avatar.text = 'Grace Hopper';
app.ts (Blueprint)
TypeScript
import { type Adw } from '@gjsify/adwaita-nativescript';
import { build } from '@gjsify/adwaita-nativescript/builder';
import tree from './avatar.blp?shared-tree';
const avatar = build(tree) as Adw.Avatar;
avatar.text = 'Grace Hopper';
Solid
TypeScript
// mount(() => <AdwAvatar />, container) — from '@gjsify/gtk-host/solid'
const AdwAvatar = () => (
<adw-avatar
text="Ada Lovelace"
size={96}
showInitials
iconName="avatar-default-symbolic"
/>
);
Vue
Vue
<!-- AdwAvatar.vue — mount(AdwAvatar, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-avatar
text="Ada Lovelace"
:size="96"
:show-initials="true"
icon-name="avatar-default-symbolic"
/>
</template>
React
TypeScript
// createRoot(container).render(<AdwAvatar />) — from '@gjsify/gtk-host/react'
const AdwAvatar = () => (
<adw-avatar
text="Ada Lovelace"
size={96}
showInitials
iconName="avatar-default-symbolic"
/>
);

Surface one in-context status message across the top of a view: a metered connection, an offline state, a pending action. A banner takes a title and an optional action button, and it reveals and hides itself, so it only occupies space while the message applies.

Adw.Banner
Blueprint
Code
using Gtk 4.0;
using Adw 1;
Adw.Banner banner {
title: _("Metered connection: updates paused");
button-label: _("Resume");
revealed: true;
}
TypeScript
TypeScript
import { build } from './banner.blp';
const { banner } = build();
banner.title = 'Metered connection: 2 updates paused';
Web Components
index.html
HTML
<adw-banner
id="banner"
title="Metered connection: updates paused"
button-label="Resume"
revealed
></adw-banner>
<script type="module">
import '@gjsify/adwaita-web';
const banner = document.querySelector('#banner');
banner.setAttribute('title', 'Metered connection: 2 updates paused');
</script>
main.js (Blueprint)
JavaScript
import { mountSharedTree } from '@gjsify/adwaita-web';
import tree from './banner.blp?shared-tree';
mountSharedTree(tree);
const banner = document.querySelector('#banner');
banner.setAttribute('title', 'Metered connection: 2 updates paused');
NativeScript
views/banner.xml
XML
<adw:Banner
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adw"
id="banner"
title="Metered connection: updates paused"
buttonLabel="Resume"
revealed="true"
/>
app.ts
TypeScript
import { type Adw } from '@gjsify/adwaita-nativescript';
import { Builder } from '@nativescript/core';
const banner = Builder.load({ path: '~/views', name: 'banner' }) as Adw.Banner;
banner.title = 'Metered connection: 2 updates paused';
app.ts (Blueprint)
TypeScript
import { type Adw } from '@gjsify/adwaita-nativescript';
import { build } from '@gjsify/adwaita-nativescript/builder';
import tree from './banner.blp?shared-tree';
const banner = build(tree) as Adw.Banner;
banner.title = 'Metered connection: 2 updates paused';
Solid
TypeScript
// mount(() => <AdwBanner />, container) — from '@gjsify/gtk-host/solid'
const AdwBanner = () => (
<adw-banner
title="Metered connection: updates paused"
buttonLabel="Resume"
revealed
/>
);
Vue
Vue
<!-- AdwBanner.vue — mount(AdwBanner, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-banner
title="Metered connection: updates paused"
button-label="Resume"
:revealed="true"
/>
</template>
React
TypeScript
// createRoot(container).render(<AdwBanner />) — from '@gjsify/gtk-host/react'
const AdwBanner = () => (
<adw-banner
title="Metered connection: updates paused"
buttonLabel="Resume"
revealed
/>
);

Draw a keyboard shortcut as keycaps instead of writing it out in prose. You give it one accelerator string and it renders the keys in GTK’s fixed order, whatever order you wrote the modifiers in.

The four levels of an accelerator

A space lists alternatives, ... is a range, + means the shortcuts are pressed in sequence, and & means they are held together. An empty accelerator shows the disabled-text placeholder instead.

Adw.ShortcutLabel
Blueprint
Code
using Gtk 4.0;
using Adw 1;
Gtk.Box shortcuts {
orientation: horizontal;
spacing: 24;
Adw.ShortcutLabel copy_shortcut {
accelerator: "<Control>C";
}
Adw.ShortcutLabel {
accelerator: "<Shift>A Home";
}
Adw.ShortcutLabel {
accelerator: "<Alt>1...9";
}
Adw.ShortcutLabel {
accelerator: "Control_L&Control_R";
}
Adw.ShortcutLabel {
accelerator: "";
disabled-text: _("Disabled");
}
}
TypeScript
TypeScript
import { build } from './shortcut-label.blp';
const { copy_shortcut } = build();
copy_shortcut.accelerator = '<Control><Shift>C';
Web Components
index.html
HTML
<gtk-box id="shortcuts" orientation="horizontal" spacing="24">
<adw-shortcut-label
id="copy_shortcut"
accelerator="<Control>C"
></adw-shortcut-label>
<adw-shortcut-label accelerator="<Shift>A Home"></adw-shortcut-label>
<adw-shortcut-label accelerator="<Alt>1...9"></adw-shortcut-label>
<adw-shortcut-label
accelerator="Control_L&amp;Control_R"
></adw-shortcut-label>
<adw-shortcut-label
accelerator=""
disabled-text="Disabled"
></adw-shortcut-label>
</gtk-box>
<script type="module">
import '@gjsify/adwaita-web';
const copy = document.querySelector('#copy_shortcut');
copy.setAttribute('accelerator', '<Control><Shift>C');
</script>
main.js (Blueprint)
JavaScript
import { mountSharedTree } from '@gjsify/adwaita-web';
import tree from './shortcut-label.blp?shared-tree';
mountSharedTree(tree);
const copy = document.querySelector('#copy_shortcut');
copy.setAttribute('accelerator', '<Control><Shift>C');
NativeScript
views/shortcut-label.xml
XML
<gtk:Box
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adw"
xmlns:gtk="~/gtk"
id="shortcuts"
orientation="horizontal"
spacing="24"
>
<adw:ShortcutLabel id="copy_shortcut" accelerator="&lt;Control>C" />
<adw:ShortcutLabel accelerator="&lt;Shift>A Home" />
<adw:ShortcutLabel accelerator="&lt;Alt>1...9" />
<adw:ShortcutLabel accelerator="Control_L&amp;Control_R" />
<adw:ShortcutLabel accelerator="" disabledText="Disabled" />
</gtk:Box>
app.ts
TypeScript
import { type Adw, type Gtk } from '@gjsify/adwaita-nativescript';
import { Builder } from '@nativescript/core';
const box = Builder.load({ path: '~/views', name: 'shortcut-label' }) as Gtk.Box;
const copy = box.getViewById<Adw.ShortcutLabel>('copy_shortcut')!;
copy.accelerator = '<Control><Shift>C';
app.ts (Blueprint)
TypeScript
import { type Adw, type Gtk } from '@gjsify/adwaita-nativescript';
import { build } from '@gjsify/adwaita-nativescript/builder';
import tree from './shortcut-label.blp?shared-tree';
const box = build(tree) as Gtk.Box;
const copy = box.getViewById<Adw.ShortcutLabel>('copy_shortcut')!;
copy.accelerator = '<Control><Shift>C';
Solid
TypeScript
// mount(() => <AdwShortcutLabel />, container) — from '@gjsify/gtk-host/solid'
const AdwShortcutLabel = () => (
<adw-shortcut-label accelerator="<Control>C" />
);
Vue
Vue
<!-- AdwShortcutLabel.vue — mount(AdwShortcutLabel, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-shortcut-label accelerator="<Control>C" />
</template>
React
TypeScript
// createRoot(container).render(<AdwShortcutLabel />) — from '@gjsify/gtk-host/react'
const AdwShortcutLabel = () => (
<adw-shortcut-label accelerator="<Control>C" />
);

Show that work is running when you cannot say how much is left. The spinner turns continuously while visible and scales to any size, from an inline glyph to a page-filling placeholder. If you do know the progress, use a progress bar instead.

Adw.Spinner
Blueprint
Code
using Gtk 4.0;
using Adw 1;
Adw.Spinner {
width-request: 48;
height-request: 48;
}
TypeScript
TypeScript
import Adw from 'gi://Adw?version=1';
import Gtk from 'gi://Gtk?version=4.0';
const spinner = new Adw.Spinner({
widthRequest: 48,
heightRequest: 48,
halign: Gtk.Align.CENTER,
valign: Gtk.Align.CENTER,
});
Web Components
HTML
<adw-spinner size="48"></adw-spinner>
NativeScript
XML
<adw:Spinner
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adw"
spinning="true"
size="48"
/>
Solid
TypeScript
// mount(() => <AdwSpinner />, container) — from '@gjsify/gtk-host/solid'
const AdwSpinner = () => (
<adw-spinner
widthRequest={48}
heightRequest={48}
/>
);
Vue
Vue
<!-- AdwSpinner.vue — mount(AdwSpinner, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-spinner
:width-request="48"
:height-request="48"
/>
</template>
React
TypeScript
// createRoot(container).render(<AdwSpinner />) — from '@gjsify/gtk-host/react'
const AdwSpinner = () => (
<adw-spinner
widthRequest={48}
heightRequest={48}
/>
);

Fill an empty, error or welcome screen with a centred placeholder: a large symbolic icon, a title and a description. Add a child widget to give the user a way out, such as a button that creates their first document.

Adw.StatusPage
Blueprint
Code
using Gtk 4.0;
using Adw 1;
Adw.StatusPage {
icon-name: "folder-symbolic";
title: _("No Documents");
description: _("Documents you create or open will appear here.");
child: Gtk.Button new_button {
label: _("New Document");
halign: center;
styles ["pill", "suggested-action"]
};
}
TypeScript
TypeScript
import { build } from './status-page.blp';
const { new_button } = build();
new_button.connect('clicked', () => console.log('Create a document'));
Web Components
index.html
HTML
<adw-status-page
icon-name="folder-symbolic"
title="No Documents"
description="Documents you create or open will appear here."
>
<gtk-button
id="new_button"
label="New Document"
halign="center"
class="pill suggested-action"
slot="child"
></gtk-button>
</adw-status-page>
<script type="module">
import '@gjsify/adwaita-web';
const button = document.querySelector('#new_button');
button.addEventListener('click', () => console.log('Create a document'));
</script>
main.js (Blueprint)
JavaScript
import { mountSharedTree } from '@gjsify/adwaita-web';
import tree from './status-page.blp?shared-tree';
mountSharedTree(tree);
const button = document.querySelector('#new_button');
button.addEventListener('click', () => console.log('Create a document'));
NativeScript
views/status-page.xml
XML
<adw:StatusPage
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adw"
xmlns:gtk="~/gtk"
iconName="folder-symbolic"
title="No Documents"
description="Documents you create or open will appear here."
>
<adw:StatusPage.child>
<gtk:Button
id="new_button"
label="New Document"
halign="center"
styleClasses="pill suggested-action"
/>
</adw:StatusPage.child>
</adw:StatusPage>
app.ts
TypeScript
import { type Adw, type Gtk } from '@gjsify/adwaita-nativescript';
import { Builder } from '@nativescript/core';
const statusPage = Builder.load({ path: '~/views', name: 'status-page' }) as Adw.StatusPage;
const button = statusPage.getViewById<Gtk.Button>('new_button')!;
button.connect('clicked', () => console.log('Create a document'));
app.ts (Blueprint)
TypeScript
import { type Adw, type Gtk } from '@gjsify/adwaita-nativescript';
import { build } from '@gjsify/adwaita-nativescript/builder';
import tree from './status-page.blp?shared-tree';
const statusPage = build(tree) as Adw.StatusPage;
const button = statusPage.getViewById<Gtk.Button>('new_button')!;
button.connect('clicked', () => console.log('Create a document'));
Solid
TypeScript
// mount(() => <AdwStatusPage />, container) — from '@gjsify/gtk-host/solid'
const AdwStatusPage = () => (
<adw-status-page
iconName="folder-symbolic"
title="No Documents"
description="Documents you create or open will appear here."
>
<gtk-button
label="New Document"
cssClasses={['pill', 'suggested-action']}
halign="center"
/>
</adw-status-page>
);
Vue
Vue
<!-- AdwStatusPage.vue — mount(AdwStatusPage, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-status-page
icon-name="folder-symbolic"
title="No Documents"
description="Documents you create or open will appear here."
>
<gtk-button
label="New Document"
:css-classes="['pill', 'suggested-action']"
halign="center"
/>
</adw-status-page>
</template>
React
TypeScript
// createRoot(container).render(<AdwStatusPage />) — from '@gjsify/gtk-host/react'
const AdwStatusPage = () => (
<adw-status-page
iconName="folder-symbolic"
title="No Documents"
description="Documents you create or open will appear here."
>
<gtk-button
label="New Document"
cssClasses={['pill', 'suggested-action']}
halign="center"
/>
</adw-status-page>
);

The title widget a header bar expects: a title with an optional subtitle, already sized and styled for that spot. Leave the subtitle empty and it collapses, so a single-line title stays vertically centred.

Adw.WindowTitle
Blueprint
Code
using Gtk 4.0;
using Adw 1;
Adw.WindowTitle window_title {
title: _("Inbox");
subtitle: _("3 unread messages");
}
TypeScript
TypeScript
import { build } from './window-title.blp';
const { window_title } = build();
window_title.subtitle = '4 unread messages';
Web Components
index.html
HTML
<adw-window-title
id="window_title"
title="Inbox"
subtitle="3 unread messages"
></adw-window-title>
<script type="module">
import '@gjsify/adwaita-web';
const windowTitle = document.querySelector('#window_title');
windowTitle.setAttribute('subtitle', '4 unread messages');
</script>
main.js (Blueprint)
JavaScript
import { mountSharedTree } from '@gjsify/adwaita-web';
import tree from './window-title.blp?shared-tree';
mountSharedTree(tree);
const windowTitle = document.querySelector('#window_title');
windowTitle.setAttribute('subtitle', '4 unread messages');
NativeScript
views/window-title.xml
XML
<adw:WindowTitle
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adw"
id="window_title"
title="Inbox"
subtitle="3 unread messages"
/>
app.ts
TypeScript
import { type Adw } from '@gjsify/adwaita-nativescript';
import { Builder } from '@nativescript/core';
const windowTitle = Builder.load({ path: '~/views', name: 'window-title' }) as Adw.WindowTitle;
windowTitle.subtitle = '4 unread messages';
app.ts (Blueprint)
TypeScript
import { type Adw } from '@gjsify/adwaita-nativescript';
import { build } from '@gjsify/adwaita-nativescript/builder';
import tree from './window-title.blp?shared-tree';
const windowTitle = build(tree) as Adw.WindowTitle;
windowTitle.subtitle = '4 unread messages';
Solid
TypeScript
// mount(() => <AdwWindowTitle />, container) — from '@gjsify/gtk-host/solid'
const AdwWindowTitle = () => (
<adw-window-title
title="Inbox"
subtitle="3 unread messages"
/>
);
Vue
Vue
<!-- AdwWindowTitle.vue — mount(AdwWindowTitle, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-window-title
title="Inbox"
subtitle="3 unread messages"
/>
</template>
React
TypeScript
// createRoot(container).render(<AdwWindowTitle />) — from '@gjsify/gtk-host/react'
const AdwWindowTitle = () => (
<adw-window-title
title="Inbox"
subtitle="3 unread messages"
/>
);
  • Boxed Lists: the row widgets for settings and detail screens.
  • Feedback: toasts and dialogs, for messages that interrupt.
  • Layout: the header bar a WindowTitle goes into.
  • Adwaita Storybook: the same widgets in a live component browser you can poke at.