- Go 98.9%
- Shell 1.1%
wlbar
A small, fast status bar for the River and niri Wayland compositors — configured with a handful of TOML keys instead of CSS + JSON. One config file, sensible defaults, colours from a base16 scheme, icons from any Nerd Font.
wlbar is a vibe-coded project — most of it was written by pairing with an AI coding agent, then reviewed and tested. It targets River's legacy
zriver_status_manager_v1/zriver_control_v1protocols — theriver-classicpackage (River 0.3.x). The rewrittenriver0.4+ drops those protocols and is not supported. Bug reports and patches welcome.
Scope — what wlbar is (and isn't)
wlbar is a status bar with a fixed, predictable feature set that works out of the box, built to stay small and dependency-light. It draws itself directly on a Wayland layer-shell surface.
- No GTK, no Qt, no widget toolkit — and there never will be. Please don't request features or modules that pull one in.
- Not waybar. It is deliberately not a scriptable, plugin-driven, CSS-themed bar. The module set and instruction set are fixed on purpose; new capability has to earn its place against complexity and dependencies.
- Minimal dependencies, minimal config. A handful of TOML keys, conservative defaults, and only the externals a module you actually use needs.
Feature requests that fit this scope (a built-in module following the existing pattern) are welcome; requests for a toolkit dependency or a user-extension mechanism are settled non-goals — see CONTRIBUTING.md.
Styles you can build
Pill geometry and corner radius come from your config; the colours come from a base16 scheme. Each example below is [theme] base16 = "..." plus a few geometry overrides — same modules, different look.
Rounded pills — examples/rounded-pills.toml (Catppuccin Mocha)
Square blocks — examples/square-blocks.toml (Tokyo Night) — radius = 0, zero margins, full-height blocks.
Minimal flat — examples/minimal-flat.toml (Nord) — no [[pill]] groups, just coloured text.
Gruvbox soft — examples/gruvbox.toml — warm palette, small corner radius.
Numbered tag cells — examples/numbered-tags.toml (Rosé Pine) — [tags] dots = false.
Classic DWM — examples/dwm.toml (Dracula) — 22 px, no pills, numbered cells, hide_icons = true, the iconic #005577 focused tag.
Each file is ~20–45 lines of TOML, mostly geometry. Copy the closest one and swap the base16 line.
Install
go install codeberg.org/vighd/wlbar/cmd/wlbar@latest
Or from the repo:
git clone https://codeberg.org/vighd/wlbar && cd wlbar && go build ./cmd/wlbar
Arch (AUR): wlbar (latest release) or wlbar-git (tip of main):
yay -S wlbar # or wlbar-git
(PKGBUILDs also live under packaging/aur/.)
The renderer rasterises glyphs through FreeType via cgo, so a C toolchain and the FreeType headers must be present when building — freetype2 on Arch, libfreetype6-dev on Debian/Ubuntu.
Running
Start it from River's init (~/.config/river/init, executable) so it comes up with the session:
wlbar &
It attaches a bar to every output and follows monitors as they hot-plug.
Dependencies
Core:
| What | Arch package |
|---|---|
| Wayland compositor — River 0.3.x | river-classic |
fontconfig (fc-match) |
fontconfig |
| FreeType (build + runtime) | freetype2 |
| a monospace TTF/OTF font | ttf-jetbrains-mono (or any) |
| a Nerd Font for icons | ttf-nerd-fonts-symbols |
| Go 1.26+ (build only) | go |
Per-module externals, needed only if you use that module:
| Module | Needs | Arch package |
|---|---|---|
audio |
wpctl; pactl for instant updates |
wireplumber; libpulse |
bluetooth |
bluetoothctl + BlueZ |
bluez-utils |
pacman |
checkupdates |
pacman-contrib |
network |
ping; iwgetid or iwctl for SSID |
iputils; wireless_tools or iwd |
mpris |
a running MPRIS2 player | — |
A module whose dependency is missing renders nothing rather than crashing the bar, so you only install what you use. The AUR packages list these under optdepends.
Configuration
wlbar reads ~/.config/wlbar/config.toml (or $XDG_CONFIG_HOME/wlbar/config.toml). With no file it runs on defaults — the bar shows up immediately and you tweak from there. The minimum is:
[layout]
left = ["rivertags"]
center = ["clock"]
right = ["battery", "network"]
wlbar --print-config dumps the effective config (yours overlaid on defaults), and a typo'd key prints a startup warning. The annotated config.sample.toml explains every key.
Modules go in three groups — left / center / right. Give a side a background in [pills], or declare explicit [[pill]] groups, to wrap it in a rounded pill.
Reloading: edit the config and pkill -HUP wlbar — it re-reads and restarts in place. A broken edit is logged and the previous config kept, so a typo won't drop your bar. SIGINT / SIGTERM quit.
base16 schemes
Seed the whole palette from a base16 scheme instead of hand-tuning [colors]:
[theme]
base16 = "gruvbox-dark-medium"
The scheme drives the bar background, accents, tag / urgent / low states, and a default colour per module (cpu green, memory yellow, temperature cyan, ...), so a themed bar is colourful with no per-module fg. Any [[pill]] without its own bg is filled from the scheme too. Explicit [colors] and [<module>] fg keys still override it.
wlbar ships no schemes — drop a .yaml in ~/.config/wlbar/themes/<name>.yaml and reference it by <name> (a direct path works too). Both the tinted-theming/schemes and archived base16-schemes formats are read; six ready-to-copy schemes ship under examples/themes/.
For a translucent bar, set just the alpha and leave the colour to the scheme:
[colors]
background_opacity = 0.0 # 0.0 transparent (pills + text only) ... 1.0 solid
[pills] opacity does the same for the pills, so a transparent bar can show the wallpaper straight through them too:
[pills]
opacity = 0.0 # 0.0 transparent ... 1.0 solid; leaves each pill's colour intact
Modules
| Name | Shows (hover detail in parentheses) | Reads from |
|---|---|---|
clock |
local time (month calendar) | time.Now() |
rivertags |
River tag indicators, dots or numbered | River status protocol |
title |
focused window title (untruncated) | River status protocol |
niriworkspaces |
niri workspace indicators, dots or numbered | niri IPC ($NIRI_SOCKET) |
nirititle |
focused window title (untruncated) | niri IPC ($NIRI_SOCKET) |
battery |
percentage + icon (time to empty/full) | /sys/class/power_supply |
network |
connection state + link speed (SSID, IP, ping) | /sys/class/net, ping |
audio |
volume / mute | wpctl |
cpu |
load % (per-core breakdown) | /proc/stat |
memory |
used / total (detail + swap) | /proc/meminfo |
temperature |
hwmon temp, critical highlight (every sensor) | /sys/class/hwmon |
disk |
usage of a mount (per-mount) | statfs |
vpn |
tunnel up/down (IP) | /sys/class/net/<iface> |
pacman |
pending updates (the list) | checkupdates |
bluetooth |
connected devices (battery + MAC) | bluetoothctl + BlueZ |
mpris |
artist/title + transport buttons (full title) | MPRIS2 D-Bus |
Most modules show extra detail in a hover popup — noted above — using the bar's own font and palette; no config needed. A module with no data renders nothing, so listing mpris or vpn is safe even when idle.
Click / scroll: left-click a rivertags tag to focus it (right-click toggles, middle-click toggles view); left-click a niriworkspaces cell to focus that workspace; click the mpris ◀ ▶ ⏯ buttons to drive the player; left-click audio to mute, scroll to change volume.
niri: swap rivertags → niriworkspaces and title → nirititle in your layout; the niri modules reuse the [tags] and [title] config sections, so dots/numbered styling and colours carry over. They read niri's JSON IPC over $NIRI_SOCKET (set automatically inside a niri session) and render nothing when that socket is absent, so listing them under River is harmless.
Troubleshooting
- "no wl_output advertised" — you're not on a Wayland session, or no compositor is running.
- Text looks wrong — check
fc-match "<your font>"resolves to a real font file. - River modules empty — needs
zriver_status_manager_v1, i.e. theriver-classicpackage (River 0.3.x). The rewrittenriver0.4+ dropped these protocols.
When filing bugs, include wlbar --version, wlbar --print-config, and your River version.
Contributing
See CONTRIBUTING.md for dev setup and tests. MIT licensed — see LICENSE.