Load from your wezterm.lua with:

June 29, 2026 · View on GitHub

widgets.wez

widgets.wez

Cross-platform status bar widgets for WezTerm.
CPU, RAM, battery, network, and disk — ready to render, ready to compose.

Platforms License OpenSSF Scorecard


Overview

widgets.wez provides ready-to-use status bar widgets for WezTerm. Every widget is cross-platform, gracefully degrades on failure, and composes with native status areas and third-party plugins like tabline.wez.

  • Eight widgets — CPU, RAM, battery, network download/upload, disk space, disk read/write
  • Cross-platform — macOS, Linux, and Windows with platform-native data sources
  • Degrades gracefully — failures produce fallback values (--%, 00 kB), never crashes
  • Composable — use with apply_to_config, manual handlers, or tabline.wez sections
  • Efficient — conservative throttling (2–30 s), shared samples between paired widgets

Screenshots

Widgets on left status
Left status placement
Widgets on right status
Right status placement

Widgets

WidgetAPIThrottleFallback
CPUsys.cpu.utilization.widget(opts)3 s--%
RAMsys.ram.utilization.widget(opts)3 s--%
Batterysys.battery.charge.widget(opts)5 s--%
Network ↓sys.network.download.widget(opts)2 s00 kB
Network ↑sys.network.upload.widget(opts)2 s00 kB
Disk spacesys.disk.space.widget(opts)30 s--%
Disk readsys.disk.read.widget(opts)3 s00 kB
Disk writesys.disk.write.widget(opts)3 s00 kB
-- Example status bar output:
  85% |  23% |  42% |  ↧ 1.2 MB |  ↥ 05 kB

Installation

Add to your wezterm.lua:

local sys = wezterm.plugin.require("https://github.com/usrivastava92/widgets.wez")

Type annotations

LuaLS type annotations for widgets.wez are available through wezterm-types. If you use wezterm-types, annotate the plugin import like this:

---@type Widgets
local sys = wezterm.plugin.require("https://github.com/usrivastava92/widgets.wez")

Updating

WezTerm caches plugins locally and does not auto-update them. After pulling new changes from GitHub, run this from the WezTerm debug overlay (Ctrl+Shift+L → type the command):

wezterm.plugin.update_all()

Then restart WezTerm (or reload your config with wezterm.reload_configuration()).

To pin a specific version (recommended for stability):

-- Pin to a git tag:
local sys = wezterm.plugin.require("https://github.com/usrivastava92/widgets.wez", { tag = "v1.0.0" })

-- Pin to a specific branch:
local sys = wezterm.plugin.require("https://github.com/usrivastava92/widgets.wez", { ref = "main" })

Local development (file URL)

-- Ensure package.path can resolve plugin/systems/init.lua, then:
package.path = "<path-to-widgets.wez>/plugin/?.lua;" .. package.path
local sys = require("systems.init")

Requirement: macOS, Linux, or Windows. Your terminal font should include Nerd Font symbols for widget icons, or override with icon = false on individual widgets.


Quick Start

Drop this into your wezterm.lua for instant status bar widgets:

local sys = wezterm.plugin.require("https://github.com/usrivastava92/widgets.wez")

sys.apply_to_config(config, {
  right = {
    sys.battery.charge.widget(),
    sys.cpu.utilization.widget(),
    sys.ram.utilization.widget(),
    sys.network.download.widget(),
    sys.network.upload.widget(),
  },
  separator = { text = "|", color = "#3b4261" },
})

apply_to_config handles the update-status handler, widget rendering, and separator placement. Empty left/right arrays are valid; if both are empty, no handler is installed.


Widget Options

Every widget accepts the same options shape:

OptionTypeDefaultDescription
iconstring | false(per widget)Nerd Font glyph or false to hide
colorstring(per widget)Foreground hex color
throttlenumber(per widget)Minimum seconds between refreshes
sys.cpu.utilization.widget({ icon = false, color = "#ff0000", throttle = 5 })
-- Renders a bare value: " 23%"

Default colors:

WidgetColor
Battery#9ece6a
CPU#7dcfff
RAM#bb9af7
Network ↓#f7768e
Network ↑#e0af68
Disk space#ff9e64
Disk read#73daca
Disk write#ff9e64

Widget Object

Every widget(opts) call returns a widget object with four members:

MemberTypeDescription
widget.get_text()fn() -> stringPlain value string. Safe as bare function reference.
widget.get_formatted()fn() -> {FormatItem}wezterm.format table list with foreground color.
widget.optstableResolved options (defaults merged with overrides).
widget.namestringStable id: "cpu.utilization", "ram.utilization", etc.

get_text() and get_formatted() are closures — they require no self or : syntax:

local cpu = sys.cpu.utilization.widget()
cpu.get_text()      -- " 23%"
cpu.opts.color      -- "#7dcfff"
cpu.name            -- "cpu.utilization"

Usage Modes

apply_to_config installs an update-status handler:

sys.apply_to_config(config, {
  left  = { sys.cpu.utilization.widget(), sys.ram.utilization.widget() },
  right = { sys.battery.charge.widget() },
  separator = { text = "|", color = "#3b4261" },
})

Manual Handler

Keep full control of the update-status handler:

local cpu = sys.cpu.utilization.widget()
local ram = sys.ram.utilization.widget()

wezterm.on("update-status", function(window, pane)
  window:set_right_status(wezterm.format({
    unpack(cpu.get_formatted()),
    { Text = "|" },
    unpack(ram.get_formatted()),
  }))
end)

tabline.wez Integration

Pass get_text closures directly into tabline sections:

local tabline = wezterm.plugin.require("https://github.com/michaelbrusegard/tabline.wez")

tabline.setup({
  sections = {
    tabline_x = { sys.cpu.utilization.widget().get_text },
    tabline_y = { sys.network.download.widget().get_text },
    tabline_z = { sys.battery.charge.widget().get_text },
  },
})

Platform Details

WidgetmacOSLinuxWindows
CPUtop -l 2 -n 0 -s 1/proc/stat deltaGet-CimInstance Win32_Processor
RAMvm_stat + sysctl/proc/meminfo (MemAvailable)Get-CimInstance Win32_OperatingSystem
Batterywezterm.battery_info()samesame
Networknetstat -ibn per-interface/proc/net/dev per-interfacenetstat -e system-wide
Disk spacedf -g /System/Volumes/Datadf -h /Win32_LogicalDisk C:
Disk I/Oioreg block storage counters/proc/diskstats root device countersWin32_PerfRawData_PerfDisk_PhysicalDisk _Total counters

Disk I/O note: Disk read/write widgets are delta-based. The first sample returns 00 kB; subsequent samples report per-second rates when counters are available.


Performance

  • Throttling — default intervals from 2 s (network) to 30 s (disk space) minimize command execution.
  • Shared samples — network download/upload share one platform sample. Disk read/write share one platform sample. No duplicate commands.
  • Global state — all persistent data is namespaced under wezterm.GLOBAL.widgets_* to avoid collisions with other plugins or user code.

Troubleshooting

Plugin not reflecting latest GitHub changes?

  • WezTerm caches plugins locally. Run wezterm.plugin.update_all() from the debug overlay (Ctrl+Shift+L) to pull the latest version.
  • To force a clean re-clone, delete the plugin cache: rm -rf ~/Library/Application\ Support/wezterm/plugins/ (macOS) or rm -rf ~/.local/share/wezterm/plugins/ (Linux).
  • Consider pinning to a git tag for reproducible installs: { tag = "v1.0.0" }.

Widgets not appearing?

  • Confirm the plugin is loaded before return config.
  • Check that status_update_interval is set (default: 1000 ms).
  • If using local dev, verify package.path includes <path-to-widgets.wez>/plugin/?.lua.

Fallback values showing (--%, 00 kB)?

  • Widgets return fallback values on command failure, parse failure, or unsupported platforms.
  • Delta-based widgets return fallback on the first sample because there is no prior data for rate calculation. This includes network throughput and disk I/O, plus Linux CPU utilization.

Icons missing or showing as boxes?

  • Ensure your WezTerm font includes Nerd Font symbols.
  • Set icon = false on any widget to render bare values without glyphs.

For Developers

git clone https://github.com/usrivastava92/widgets.wez
# Load from your wezterm.lua with:
# package.path = "<path>/plugin/?.lua;" .. package.path
# local sys = require("systems.init")

License

MIT