Using GtkListBox with AdwTabView

Thanks to Gio.ListModel it is surprisingly simple to use a Gtk.ListBox as a vertical list of tabs for an Adw.TabView.

The benefit over using a simple Gtk.Stack is that Adw.TabView supports standard shortcuts for switching view.

Here is a GJS example that should translate well to other languages. You can try this directly in Workbench.

// This snippet goes into Workbench UI Blueprint

using Gtk 4.0;
using Adw 1;

Box {
  Gtk.ScrolledWindow {
    width-request: 200;
    Gtk.ListBox list_box {
      selection-mode: browse;
  Gtk.Separator {}
  Adw.TabView tab_view {}
// This snippet goes into Workbench Code JavaScript

import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw?version=1";

import GObject from "gi://GObject";


const tab_view = workbench.builder.get_object("tab_view");
const list_box = workbench.builder.get_object("list_box");

// Create a binding between the Gtk.ListBox and the Adw.TabView

  // This function will be called for every new Adw.TabPage
  (tab_page) => {
    return buildTabRow(tab_page);

list_box.connect("row-selected", (self, row) => {

tab_view.connect("notify::selected-page", (self, page) => {

// Add some Adw.TabPage and let the binding do the rest

for (let i = 1; i < 11; i++) {
  const title = `hello ${i}`;
  const tab_page = tab_view.append(
    new Adw.StatusPage({
      hexpand: true,
  tab_page.title = title;

// You probably should use a custom widget
// but this will do it for the purpose of this example

function buildTabRow(tab_page) {
  const list_box_row = new Gtk.ListBoxRow({
    selectable: true,
  list_box_row.tab_page = tab_page;
  const label = new Gtk.Label();

  tab_page.list_box_row = list_box_row;


  return list_box_row;