How to install, configure and customize Waybar on Linux

Waybar is an highly customizable Wayland bar for Sway and other Wlroots-based compositors, such as Hyprland or River. The many available functionalities of Waybar are organized in modules, which can be easily configured and styled.

In this tutorial we learn how to install and configure Waybar on some of the most used Linux distributions.

In this tutorial you will learn:

  • How to install Waybar
  • How to configure and style Waybar and some of the most useful modules
How to install, configure and customize Waybar on Linux
How to install, configure and customize Waybar on Linux – original image by rawpixel.com on Freepik
Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Any modern Linux distribution
Software Waybar
Other None
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux-commands to be executed as a regular non-privileged user

Installation

Waybar is free and open source software; the code is available on GitHub, but all major Linux distributions include a pre-compiled version of the bar, ready to be installed, in their official repositories. To install Waybar on the latest Debian stable (Bookworm) and other Debian-based distribution, we can run:

$ sudo apt install waybar

On Fedora, instead, we can install the “waybar” package by using dnf:

$ sudo dnf install waybar

Waybar is also available in the Archlinux “Extra” repository. We can install it with pacman:

$ sudo pacman -Sy waybar

Waybar configuration

Waybar uses a JSONC (JSON with comments) file format for its configuration, and looks for a configuration file in the following locations, in priority order:

  • $XDG_CONFIG_HOME/waybar/config
  • ~/.config/waybar/config
  • ~/.waybar/config
  • /etc/xdg/waybar/config

Before start talking about modules and how to configure them, we want to write the “general” Waybar configuration. Here is an example:

{
  "layer": "bottom",
  "position": "top",
  "height": 24,
  "spacing": 5,
  "modules-left": ["sway/workspaces","sway/mode"],
  "modules-center": ["sway/window"],
  "modules-right": ["idle_inhibitor","cpu","memory","battery","pulseaudio","clock","tray"]
}

By using the layer key, we specify whether the bar should be displayed on top or behind other windows. In tiling window manager environments, this becomes relevant when using utilities like “dmenu”, to make sure the menu window is visible, if, when invoked, it appears in the same position of the bar. Valid values are “top” and “bottom”, which is the default. With the position key, we declare on which side of the screen the bar should be placed. Valid options are: “top”, “bottom”, “left”, “right”.



Both the height and spacing keys take an integer value which specifies the height of the bar, and the amount of space between modules, respectively. Finally, the modules_left, modules_center and modules_right keys let us specify the modules we want to use in the corresponding segments of the bar. Each one of those keys accept an array of modules names as value.

Waybar modules

Waybar comes with a vast selection of modules. Although it is impossible to see all of them in this tutorial, let’s at least take a look at those we included in the configuration above.

The “sway/workspaces” module

The sway/workspaces module shows the workspaces currently in use in the Sway window manager. By default, only workspaces numbers are displayed; this, however, can be changed by using the format key and the available placeholders. Among the others:

  • {value}: replaced by the name of the workspace as defined in the sway configuration in its entirety
  • {name}: replaced by the value at the right of the column if the workspace name is specified in the format “<number>:<name>”. If the workspace name is “1:TERMINAL”,  for example, the module will display: “TERMINAL”
  • {output}: replaced by the output where the workspace is located (e.g. HDMI-A-2)

The “sway/mode” module

The sway/mode module is used to display the current Sway WM mode (e.g mode “resize”). The default is just to display the mode name, which corresponds to the following configuration:

"sway/mode": { 
  "format": "{}"
},

The “sway/window” module

The sway/window module displays the title of the currently focused window. The default configuration ensures only the title of the window is displayed, by using the “{title}” placeholder as value of the format key:

"sway/window": { 
  "format": "{title}"
}

Other available placeholders are “{app_id}” which is replaced by ID of the focused app (e.g: org.kde.ghostwriter), and “{shell}”, which is substituted by the shell of the focused window: “xwayland” for windows using xwayland or “xdg-shell” for native wayland applications.

The “Idle Inhibitor” module

The Idle Inhibitor module can be used to activate the so called “presentation mode”, which involves the suspension of power saving activities such as screen blanking and locking. A click on the module displayed in Waybar toggles the presentation mode state. Here is a configuration example:

"idle_inhibitor": { 
  "format": "{icon}", 
  "format-icons": { 
    "activated": "\uf06e", 
    "deactivated": "\uf070" 
  } 
}

There are two placeholders which can be used as values of format: “{status}”, which is replaced by a string representing the status of the idle inhibitor (“actived” vs “deactivated”), and “{icon}”, which gets substituted by the corresponding icon, defined in the format-icons object.

Font-awesome fonts provide a series of really nice unicode icons which can be used in Waybar. The fonts are often packaged in the repository of the most used distributions. On Debian and derivatives, for example, the package is called “fonts-font-awesome”; on Fedora the fonts come in the “fontawesome-fonts” package, and on ArchLinux they are packaged as  “otf-font-awesome “.

Over the course of this tutorial, we represent icons with their unicode escape sequence, to make sure you are able to visualize them correctly. In your configuration, however, you can simply copy and paste the corresponding characters.

The “cpu” module

The cpu module is useful to keep an eye on CPU utilization. By default reported data is updated every 10 seconds and usage is displayed in percentage form. This is defined via the format key, using the “{usage}” placeholder:

"cpu": { 
  "interval": 10, 
  "format": "CPU: {usage}%" 
},

Some of the other placeholders we can use, are:

  • {load}: replaced by the current CPU load
  • {avg_frequency}: replaced by the current CPU average frequency in GHz for all cores
  • {min_frequency}: replaced by the minimum lowest core frequency in GHz
  • {max_frequency}: replaced by the current max frequency of the highest frequency core in GHz

The “memory” module

The memory module displays RAM and swap usage. By default only the status of the former is shown, in percentage format, using the “{percentage}” placeholder. The status is updated every 30 seconds:

"memory": { 
  "interval": 30, 
  "format": "{percentage}%" 
},

By using some of the other available placehodlers, we can display the amount of memory and swap usage on the total available. Here is an example:

"memory": {
  "interval": 30,
  "format": "RAM: {used:0.1f}GiB/{total:0.1f}GiB ({percentage}%) SWAP: {swapUsed:0.1f}GiB/{swapTotal:0.1f}GiB ({swapPercentage}%)"
},

The “{used}” placeholder is expanded in the amount of used memory expressed in GiB, while “{total}” is replaced by the total available memory. The “{swapUsed}” and “{swapTotal}” placeholders does the same for swap. Inside placeholders we specified the formatting style: “0.1f” formats the output so that only a single decimal value is displayed, for better readability.

The “battery” module

The battery module displays the current available battery charge and its status. Here is an example of its configuration:

"battery": {
  "bat": "BAT0",
  "states": {
    "good": 95,
    "warning": 30,
    "critical": 5
  },
  "format": "BAT0: {capacity}%",
  "format-charging": "BAT0: {capacity}% (charging)",
  "format-plugged": "BAT0: {capacity}% (plugged)",
},

The bat key is used to specify what battery monitor the module should use among those available in the /sys/proc/power_supply. If this option is left empty, the value is auto detected; you may want to specify it if your machine has more than one battery. The states key takes a JSON object as value; each key-value pair in it represents a status and the corresponding charge percentage treshold.



We can use the format-charging, format-plugged and format keys to define how data is formatted when the battery is charging, when the AC adaptor is plugged (having the adaptor plugged doesn’t necessary mean the battery is charging: see charging tresholds), and in all other situations, respectively.

The “clock” module

We can use the clock module to display current date and time. By default, only time is included in the output:

"clock": {
  "format": "{:%H:%M}"
},

To display also the current date, we could use the following configuration:

"clock": {
  "format": "{:%Y/%m/%d %H:%M}",
},

To display a small calendar as a tooltip, we can use the tooltip-format key, given it the following value:

"clock": {
  "format": "{:%Y/%m/%d %H:%M}",
  "tooltip-format": "<tt><small>{calendar}</small></tt>",
},

The style of the calendar can be further customized using the calendar object. To color the month name in yellow, and the currend day in red, for example, we could write:

"clock": {
  "format": "{:%Y/%m/%d %H:%M}",
  "tooltip-format": "<tt><small>{calendar}</small></tt>",
  "calendar": {
    "format": {
      "months": "<span color='#ffead3'><b>{}</b></span>",
      "today": "<span color='#ff6699'><b>{}</b></span>"
    }
  }
},

You can pretty much configure the calendar as you like. Some configuration examples are displayed on the GitHub page of the module.

The “pulseaudio” module

The pulseaudio module reports information about the current pulseaudio volume. The default configuration displays just the volume percentage:

"pulseaudio": {
  "format": "{volume}%"
}

To make things look nicer, we could display an icon beside the percentage value. We can define what icons to use depending on the volume level (low to high), using the format-icons key:

"pulseaudio": { 
  "format": "{volume}% {icon}", 
  "format-icons": { 
    "default": ["\uf026", "\uf027", "\uf028"] 
  }  
},

By scrolling the mouse wheel over the “pulseaudio” module in Waybar, it is possibile to raise and lower the volume level. It would also be nice to have a quick way to mute the audio altogether. We can do this but specifying a “pactl” command (pactl is a utility to control the Puseaudio server), when one of the “on-click”, “on-click-right”, or “on-middle-click” events are registered. In the example below we mute the audio on left mouse click, and launch pavucontrol on right click:

"pulseaudio": {
  "format": "{volume}% {icon}",
  "format-muted": " {volume}%",
  "format-icons": {
    "default": ["\uf026", "\uf027", "\uf028"]
  },
  "on-click": "pactl set-sink-mute @DEFAULT_SINK@ toggle",
  "on-click-right": "pavucontrol"
},

Finally, we may want to specify a format to be used when the audio is muted. We can do it by using the format-muted key:

"pulseaudio": {
  "format": "{icon} {volume}%",
  "format-icons": {
    "default": ["\uf026", "\uf027", "\uf028"]
  },
  "on-click": "pactl set-sink-mute @DEFAULT_SINK@ toggle",
  "on-click-right": "pavucontrol",
  "format-muted": "\uf00d {volume}%"
},

The “Tray” module

Tray bars are a foundamental part of the traditional desktop experience. Some applications are designed to be minimized in the tray when they are closed, and without one, it is impossible to get their state. Waybar supports tray functionalities via the “tray” module.

Some of the keys we can use in the the tray module configuration are: icons-size, which sets the size of icons in the tray, and spacing, which defines the space between icons. Both keys take an integer as value. Here is a typical tray configuration:

"tray": {
  "icon-size": 20,
  "spacing": 10
}

Putting it all together

Here is what our complete configuration looks like when we put all the elements together:

{
  "layer": "bottom",
  "position": "top",
  "height": 24,
  "spacing": 5,
  "modules-left": ["sway/workspaces","sway/mode"],
  "modules-center": ["sway/window"],
  "modules-right": ["idle_inhibitor","cpu","memory","battery","pulseaudio","clock","tray"],
  "sway/mode": {
    "format": "{}"
  },
  "sway/window": {
    "format": "{title}"
  },
  "idle_inhibitor": {
    "format": "{icon}",
    "format-icons": {
      "activated": "\uf06e",
      "deactivated": "\uf070"
    }
  },
  "cpu": { 
    "interval": 10, 
    "format": "CPU: {usage}%" 
  },
  "memory": {
    "interval": 30,
    "format": "RAM: {used:0.1f}GiB/{total:0.1f}GiB ({percentage}%) SWAP: {swapUsed:0.1f}GiB/{swapTotal:0.1f}GiB ({swapPercentage}%)"
  },
  "battery": {
    "bat": "BAT0",
    "states": {
      "good": 95,
      "warning": 30,
      "critical": 5
    },
    "format": "BAT0: {capacity}%",
    "format-charging": "BAT0: {capacity}% (charging)",
    "format-plugged": "BAT0: {capacity}% (plugged)",
  },
  "clock": {
    "format": "{:%Y/%m/%d %H:%M}",
    "tooltip-format": "<tt><small>{calendar}</small></tt>",
    "calendar": {
      "format": {
        "months": "<span color='#ffead3'><b>{}</b></span>",
        "today": "<span color='#ff6699'><b>{}</b></span>"
      }
    }
  },
  "pulseaudio": {
    "format": "{icon} {volume}%",
    "format-icons": {
      "default": ["\uf026", "\uf027", "\uf028"]
    },
    "on-click": "pactl set-sink-mute @DEFAULT_SINK@ toggle",
    "on-click-right": "pavucontrol",
    "format-muted": "\uf00d {volume}%"
  },
  "tray": {
    "icon-size": 20,
    "spacing": 10
  },
}

Styling Waybar

We can customize the look of Waybar by using one of the following css files:

  • ~/.config/waybar/style.css
  • ~/waybar/style.css
  • /etc/xdg/waybar/style.css

We can reference the various elements of the bar by using selectors. Let’s see some examples. Suppose we want to set the waybar background and text colors; we would reference the bar by ID:

#waybar { 
  background-color: #333333; 
  color: #ffffff; 
}

In this other example, we define the background color of the “idle_inhibitor” module, when presentation mode is active (see the “.activated” class):

#idle_inhibitor.activated { 
  background-color: #285577; 
}

To globally set the font family and their size, instead:

*{ 
  font-family: FontAwesome, Roboto, Helvetica, Arial, sans-serif; 
  font-size: 13px; 
}



Really simple and intuitive. All valid CSS classes for the modules are listed on the modules page. Just as an example, here is the style I am currently applying:

* {
    font-family: FontAwesome, Roboto, Helvetica, Arial, sans-serif;
    font-size: 13px;
}

#waybar {
    background-color: #333333;
    color: #ffffff;
}

button {
    box-shadow: inset 0 -3px transparent;
    border: none;
    border-radius: 0;
    padding: 0 5px;
}

#workspaces button {
    background-color: #5f676a;
    color: #ffffff;
}

#workspaces button:hover {
    background: rgba(0,0,0,0.2);
}

#workspaces button.focused {
    background-color: #285577;
}

#workspaces button.urgent {
    background-color: #900000;
}

#workspaces button.active {
    background-color: #285577;
}

#clock,
#battery,
#cpu,
#memory,
#pulseaudio,
#tray,
#mode,
#idle_inhibitor,
#window,
#workspaces {
    margin: 0 5px;
}


.modules-left > widget:first-child > #workspaces {
    margin-left: 0;
}


.modules-right > widget:last-child > #workspaces {
    margin-right: 0;
}

@keyframes blink {
    to {
        background-color: #ffffff;
        color: #000000;
    }
}

#battery.critical:not(.charging) {
    background-color: #f53c3c;
    color: #ffffff;
    animation-name: blink;
    animation-duration: 0.5s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    animation-direction: alternate;
}

label:focus {
    background-color: #000000;
}

#tray > .passive {
    -gtk-icon-effect: dim;
}

#tray > .needs-attention {
    -gtk-icon-effect: highlight;
    background-color: #eb4d4b;
}

#idle_inhibitor {
    font-size: 15px;
    background-color: #333333;
    padding: 5px;
}

#idle_inhibitor.activated {
    background-color: #285577;
}

And here is how the resulting Waybar looks like. Nothing too fancy, pretty ugly indeed, but hopefully it gives you the general idea. The limit in the customization is pretty much your creativity:

The customized Waybar
The customized Waybar

Conclusions

In this tutorial we saw how to install, configure and customize Waybar on Linux. Waybar is a really powerful Wayland bar for Wlroots-based compositors like Sway. We saw how to create a simple configuration file, how to use some of the many modules integrated in Waybar, and, finally, how to style the bar using a CSS stylesheet. If it is the first time you use Waybar, to avoid having to configure it from scratch, you can copy the system-wide configuration file installed as /etc/xdg/waybar/config under ~/.config/waybar, and use it as a starting point.