Widgets

Every widget HQTUI draws, what it actually looks like, and the code that drew it. The pictures are not screenshots: each one is rendered headlessly at build time by the library itself, and each snippet is a region of a program that compiles and runs. If a widget stopped drawing, this page would go blank rather than lie.

28 widgets across 11 languages, 207 worked examples. C++, Ruby, PHP and Perl reach the library through a narrower native core, so they cover the widgets that core draws rather than all of them.
LanguageWidgetsGallery
TypeScript28 / 28examples/widgets/gallery.ts
JavaScript28 / 28examples/widgets/gallery.js
Rust28 / 28ports/rust/examples/widgets.rs
Go28 / 28ports/go/examples/widgets/main.go
Python28 / 28ports/python/examples/widgets.py
Zig28 / 28ports/zig/examples/widgets.zig
C++7 / 28ports/cpp/examples/widgets.cpp
Ruby8 / 28ports/ruby/examples/widgets.rb
PHP8 / 28ports/php/examples/widgets.php
Perl8 / 28ports/perl/examples/widgets.pl
COBOL8 / 28ports/cobol/examples/widgets.cbl

Text

Text

textText

Aligned, styled, optionally wrapped copy. Every other widget is built on it.

Plain text. It fills the width it is given.               
Bold, in the theme's primary color.                       
                                            Right aligned.
Long copy wraps when you ask it to, instead of being cut  
at the edge.                                              
export function text(ui: Container, theme: Theme): void {
  ui.text("Plain text. It fills the width it is given.");
  ui.text("Bold, in the theme's primary color.", { bold: true, fg: theme.primary });
  ui.text("Right aligned.", { align: "right" });
  ui.text("Long copy wraps when you ask it to, instead of being cut at the edge.", { wrap: true });
}

Label

labelText

Text in the theme's muted color: captions and the line under a number.

cpu · 8 cores · 3.4 GHz                                   
42.1%                                                     
15 minute average                                         
export function label(ui: Container, theme: Theme): void {
  // `label` is `text` in the theme's muted color: secondary copy, captions,
  // the line under a number that says what the number is.
  ui.label("cpu · 8 cores · 3.4 GHz");
  ui.text("42.1%", { bold: true, fg: theme.success });
  ui.label("15 minute average");
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Heading

headingText

Bold text in the theme's title color, for sectioning without a panel.

Storage                                                   
Four volumes, one degraded                                
                                                          
Network                                                   
export function heading(ui: Container, theme: Theme): void {
  // `heading` is `text` in the theme's title color, bold.
  ui.heading("Storage");
  ui.label("Four volumes, one degraded");
  ui.spacer(1);
  ui.heading("Network", { fg: theme.accent });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Badge

badgeText

A status chip. Filled, subtle or outline.

 active     idle     failed                               
export function badge(ui: Container, theme: Theme): void {
  ui.row({ size: 1, gap: 1 }, (r) => {
    r.badge({ text: "active", color: theme.success, size: 10 });
    r.badge({ text: "idle", color: theme.warning, variant: "subtle", size: 8 });
    r.badge({ text: "failed", color: theme.danger, variant: "outline", size: 10 });
    r.spacer("fill");
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Divider

dividerText

A rule, optionally labelled, that titles a section cheaply.

Above the line                                            
──────────────────────────────────────────────────────────
Below it                                                  
───────────────────────── status ─────────────────────────
A labelled divider titles a section without spending a pa…
export function divider(ui: Container, theme: Theme): void {
  ui.text("Above the line");
  ui.divider();
  ui.text("Below it");
  ui.divider({ label: "status", align: "center", color: theme.accent });
  ui.text("A labelled divider titles a section without spending a panel on it");
}

Not in 1 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Key/values

keyValuesText

Aligned label and value pairs. The backbone of every system panel.

Host                                            web-01.iad
Uptime                                           18d 04:12
Load                                      0.42  0.51  0.60
Established                                          1,284
export function keyValues(ui: Container, theme: Theme): void {
  // The backbone of every "System" panel: labels left, values right.
  ui.keyValues([
    { label: "Host", value: "web-01.iad" },
    { label: "Uptime", value: "18d 04:12" },
    { label: "Load", value: "0.42  0.51  0.60", color: theme.warning },
    { label: "Established", value: "1,284", color: theme.success },
  ]);
}

Status bar

statusBarText

The keybinding strip along the bottom, with a right-hand slot.

 F1 Help  F2 Theme  F3 Filter  ^K Palette  q Quit  0.41ms  18…
export function statusBar(ui: Container, _theme: Theme): void {
  // Usually the last thing drawn, pinned to the bottom row.
  ui.statusBar({
    items: [
      { key: "F1", label: "Help" },
      { key: "F2", label: "Theme" },
      { key: "F3", label: "Filter", active: true },
      { key: "^K", label: "Palette" },
      { key: "q", label: "Quit" },
    ],
    right: [{ label: "0.41ms  184 cells" }],
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Data

Table

tableData

Columns with alignment, widths, zebra striping, selection and scroll.

Name              Size Type     Modified                  
src             4.2 KB dir        2m ago                  
test            1.1 KB dir        5m ago                  
package.json    1.2 KB file      10m ago                  
README.md       3.4 KB file       1h ago                  
                                                          
export function table(ui: Container, theme: Theme): void {
  ui.table({
    rows: [
      { name: "src", size: "4.2 KB", type: "dir", modified: "2m ago" },
      { name: "test", size: "1.1 KB", type: "dir", modified: "5m ago" },
      { name: "package.json", size: "1.2 KB", type: "file", modified: "10m ago" },
      { name: "README.md", size: "3.4 KB", type: "file", modified: "1h ago" },
    ],
    selected: 1,
    zebra: true,
    columns: [
      { key: "name", title: "Name", min: 12, color: theme.primary },
      { key: "size", title: "Size", width: 9, align: "right" },
      { key: "type", title: "Type", width: 6 },
      { key: "modified", title: "Modified", width: 10, align: "right", color: theme.muted },
    ],
  });
}

List

listData

A selectable list with bullets and its own scrollbar.

▸ apps/demo                             
▸ packages/hqtui                        
▸ apps/web                              
▸ docs                                  
                                        
export function list(ui: Container, theme: Theme): void {
  ui.list({
    items: [
      { label: "apps/demo", color: theme.primary },
      { label: "packages/hqtui" },
      { label: "apps/web" },
      { label: "docs" },
    ],
    selected: 0,
    bullet: "▸",
    scrollbar: true,
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Tree

treeData

Nested rows with expand state and per-node value columns.

└─ systemd                                1.3 
   ├─ bash                                0.1 
   ├─ bun                                32.8 
   │  └─ bun:worker                      12.4 
   └─ postgres                            6.7 
                                              
export function tree(ui: Container, _theme: Theme): void {
  ui.tree({
    nodes: [
      {
        label: "systemd",
        expanded: true,
        values: [{ text: "1.3", width: 6 }],
        children: [
          { label: "bash", values: [{ text: "0.1", width: 6 }] },
          {
            label: "bun",
            expanded: true,
            values: [{ text: "32.8", width: 6 }],
            children: [{ label: "bun:worker", values: [{ text: "12.4", width: 6 }] }],
          },
          { label: "postgres", values: [{ text: "6.7", width: 6 }] },
        ],
      },
    ],
    selected: 2,
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Log

logData

Levelled log lines that tail by default and scroll back from the end.

12:45:02 INFO  listening on :8080                             
12:45:09 WARN  slow query 412ms                 {table=users} 
12:45:11 ERROR upstream timeout                               
12:45:14 INFO  retry succeeded                                
                                                              
export function log(ui: Container, _theme: Theme): void {
  ui.log({
    entries: [
      { time: "12:45:02", level: "info", message: "listening on :8080" },
      { time: "12:45:09", level: "warn", message: "slow query 412ms", meta: "{table=users}" },
      { time: "12:45:11", level: "error", message: "upstream timeout" },
      { time: "12:45:14", level: "info", message: "retry succeeded" },
    ],
    // Lines scrolled back from the newest. 0 keeps it tailing.
    fromEnd: 0,
    scrollbar: true,
  });
}

Meters

Meter

meterMeters

A labelled bar. Smooth or segmented, heat-colored by default.

CPU ████████████████████████▊─────────────── 62%
MEM ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮ 31%
SWP ───── 87%
export function meter(ui: Container, theme: Theme): void {
  ui.meter({ label: "CPU", value: 0.62, style: "smooth", color: theme.primary });
  ui.meter({ label: "MEM", value: 0.31, style: "segmented" });
  ui.meter({ label: "SWP", value: 0.87, style: "smooth" });
}

Meters

metersMeters

A whole bank of meters in one call, laid out in columns.

P0  ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮  12%  P4  ▮▮▮▮▮▮▮▮▮▮▮▮▮▮  38%
P1  ▮▮▮▮▮▮▮▮▮▮▮▮  44%  P5  ▮▮▮▮▮▮▮▮▮▮  55%
P2  ▮▮▮▮▮▮▮  71%  P6  ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮  22%
P3  ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮   9%  P7  ▮▮▮▮▮▮▮▮  66%
export function meters(ui: Container, _theme: Theme): void {
  // One call for a whole bank. `columns` lays them out side by side.
  ui.meters(
    [0.12, 0.44, 0.71, 0.09, 0.38, 0.55, 0.22, 0.66].map((value, i) => ({ label: `P${i}`, value })),
    { columns: 2, labelWidth: 4, valueWidth: 5, style: "segmented" },
  );
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Progress

progressMeters

A determinate bar that can show a count as well as a ratio.

Indexing █████████▉────────────────────── 37/120
Upload ██████████████████████████████▍────── 82%
export function progress(ui: Container, _theme: Theme): void {
  ui.progress({ label: "Indexing", value: 37, max: 120, showCount: true });
  ui.progress({ label: "Upload", value: 0.82 });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Graph

graphMeters

A Braille line chart, optionally filled under the curve.

                                                          
                                                          
                                        ⢀⣀⠤⣀              
                      ⢀⡠⢄⡀        ⢀⠤⠒⠉⠁███⠉⠒⠤⡀          
                   ⢀⡠⠊⠁⠈⠒⠤⣀     ⡠⠒⠁██████████⠈⠑⠤⣀       
            ⢀⡠⠒⠉⠒⠢⠔⠁████████⠉⠒⠒⠉⠉███████████████⠉⠑⠒⠤⠤⠒⠒
    ⢀⡠⠤⢄⣀⡠⠔⠊⠁▆▆███████████████████████████████████████████
⣀⠤⠔⠊⠁████████████████████████████████████████████████████
██████████████████████████████████████████████████████████
export function graph(ui: Container, theme: Theme): void {
  // Braille line chart. `fill` shades the area under the curve.
  ui.graph({ values: CPU_HISTORY, min: 0, max: 100, fill: true, color: theme.success, size: "1fr" });
}

Sparkline

sparklineMeters

One row: label, inline chart and a value.

CPU                         ▁▂▃▂▃▅▄▆▇▅▄▅▆▇█▇▆▅▄▅ 44%
Mem                             ▁▂▁▃▅▄█▇▅▃▂▄▆█▇▅ 31%
Net                    ▁▂▃▂▃▅▄▆▇▅▄▅▆▇█▇▆▅▄▅ 2.4 MB/s
export function sparkline(ui: Container, theme: Theme): void {
  ui.sparkline({ label: "CPU ", values: CPU_HISTORY, text: "44%", color: theme.success });
  ui.sparkline({ label: "Mem ", values: NET_HISTORY, text: "31%", color: theme.warning });
  ui.sparkline({ label: "Net ", values: CPU_HISTORY, text: "2.4 MB/s", color: theme.primary });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Histogram

histogramMeters

Block columns. Cheaper than Braille and easier to read when short.

             ▃█▁                                          
           ▃███                                          
       ▆█▃  ████▅                                         
     ▇▂███▃▇█████▆▁▄                                      
    ▄███████████████                                      
  ▇▄████████████████                                      
▃███████████████████                                      
████████████████████                                      
export function histogram(ui: Container, theme: Theme): void {
  // Block columns. Cheaper than Braille and easier to read when short.
  ui.histogram({ values: CPU_HISTORY, color: theme.accent, size: "1fr" });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Heat bar

heatBarMeters

A segmented bar colored along the theme's heat ramp.

▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮
▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮
▮▮▮▮
export function heatBar(ui: Container, _theme: Theme): void {
  // Segmented bar colored along the theme's heat ramp, like btop's temperatures.
  ui.heatBar({ value: 0.28 });
  ui.heatBar({ value: 0.64 });
  ui.heatBar({ value: 0.91 });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Gauge

gaugeMeters

A semicircular dial. Wants at least nine columns by five rows.

                                        
                                        
                                        
           ⢀⣠⣤⡶⠶⠞⠛⠛⠛⠛⠛⠛⠛⠶⠶⣦⣤⣀           
       ⢀⣠⡶⠟⠋⠉              ⠈⠉⠛⠷⣦⣀       
     ⢀⡴⠟⠁                       ⠙⠷⣄     
   ⢀⡴⠋                            ⠈⠳⣄   
  ⣠⠟                                ⠘⢧⡀ 
 ⢠⠏                                  ⠈⢧ 
                                     ⠘⡇
⢸⠃                                     
                  62%                   
export function gauge(ui: Container, _theme: Theme): void {
  // A semicircular dial. Wants at least 9x5.
  ui.gauge({ value: 62, label: "62%" });
}

Donut

donutMeters

A ring split into labelled, colored segments.

                                        
             ⢀⣤⣴⣶⣿⣿⣿⣿⣿⣿⣷⣶⣤⣄             
           ⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀          
         ⢀⣼⣿⣿⣿⣿⣿⣿⠿⠛⠛⠛⠛⠻⢿⣿⣿⣿⣿⣿⣿⣄         
         ⣼⣿⣿⣿⣿⣿⠏        ⠈⢿⣿⣿⣿⣿⣿⡄        
        ⢠⣿⣿⣿⣿⣿⠇           ⢿⣿⣿⣿⣿⣧        
        ⠸⣿⣿⣿⣿⣿⡄           ⣿⣿⣿⣿⡿        
         ⢿⣿⣿⣿⣿⣷⡄         ⣴⣿⣿⣿⣿⣿⠇        
         ⠘⢿⣿⣿⣿⣿⣿⣷⣤⣀⣀⣀⣀⣠⣴⣿⣿⣿⣿⣿⣿⠟         
          ⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋          
            ⠈⠙⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⠉            
                 ⠉⠉⠉⠉⠉⠉⠁                
export function donut(ui: Container, theme: Theme): void {
  ui.donut({
    segments: [
      { value: 4.65, color: theme.primary, label: "Used" },
      { value: 10.96, color: theme.warning, label: "Free" },
    ],
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Inputs

Button

buttonInputs

Variants for primary, success, warning, danger and ghost. Joins the Tab order.

  Primary     Success     Danger                          
export function button(ui: Container, _theme: Theme): void {
  // Pass `onPress` and the button joins the Tab order automatically.
  ui.row({ size: 1, gap: 1 }, (r) => {
    r.button({ label: "Primary", width: 11, size: 11, onPress: () => {} });
    r.button({ label: "Success", width: 11, size: 11, variant: "success" });
    r.button({ label: "Danger", width: 10, size: 10, variant: "danger" });
    r.spacer("fill");
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Checkbox

checkboxInputs

A checkbox or a toggle, depending on the variant.

[▮ ] Toggle   [ ] Checkbox                    
export function checkbox(ui: Container, _theme: Theme): void {
  ui.row({ size: 1, gap: 2 }, (r) => {
    r.checkbox({ label: "Toggle", checked: true, variant: "toggle", size: 12 });
    r.checkbox({ label: "Checkbox", checked: false, size: 14 });
    r.spacer("fill");
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Select

selectInputs

A closed value, or an open dropdown with the current option marked.

 Dracula                              
 Dark                                   
 Dracula                                
 Nord                                   
 Tokyo Night                            
                                        
export function select(ui: Container, _theme: Theme): void {
  ui.select({
    value: "Dracula",
    width: 20,
    size: 20,
    open: true,
    options: ["Dark", "Dracula", "Nord", "Tokyo Night"],
    selectedIndex: 1,
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Text input

textInputInputs

A labelled field with a placeholder and a cursor.

Search  postgres                                    
                                                    
Filter  type to filter…                             
export function textInput(ui: Container, _theme: Theme): void {
  ui.textInput({ label: "Search", value: "postgres", size: 1 });
  ui.spacer(1);
  ui.textInput({ label: "Filter", value: "", placeholder: "type to filter…", size: 1 });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Tabs

tabsInputs

A single row of tabs with one active. Clickable.

  1 dashboard    2 traffic    3 sessions    4 network         
export function tabs(ui: Container, _theme: Theme): void {
  ui.tabs({
    tabs: ["1 dashboard", "2 traffic", "3 sessions", "4 network"],
    active: 1,
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Overlays

Command palette

commandPaletteOverlays

A query line and filtered results, over the current screen.

                                                              
                                                              
 ╭─ Command Palette ────────────────────────────────────────╮ 
 the                                                      
 ────────────────────────────────────────────────────────── 
  Toggle theme                                          F2  
  Filter processes                                      F3  
  Sort by memory                                        F6  
 ╰──────────────────────────────────────────────────────────╯ 
                                                              
export function commandPalette(ui: Container, _theme: Theme): void {
  ui.commandPalette({
    query: "the",
    items: [
      { label: "Toggle theme", hint: "F2" },
      { label: "Filter processes", hint: "F3" },
      { label: "Sort by memory", hint: "F6" },
    ],
    selected: 0,
  });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Tooltip

tooltipOverlays

A small floating box anchored at a cell.

Tooltips are overlays positioned at a cell, for hover and hin…
                                                              
                                                              
      ╭──────────────────╮                                    
      swap is 87% full                                      
      ╰──────────────────╯                                    
                                                              
export function tooltip(ui: Container, _theme: Theme): void {
  ui.text("Tooltips are overlays positioned at a cell, for hover and hints.");
  ui.tooltip({ text: "swap is 87% full", x: 6, y: 3 });
}

Not in 5 of the 11 ports yet. C++, Ruby, PHP and Perl reach the library through a narrower native core.

Want the longer version? The High Quality Terminal UI Cookbook walks through building a real dashboard with these.