The status bar Xmobar works out of the box on the top edge of your screen and shows useful system info. However, if you want to make it look nice and change what information to show, you can customize it with a tiny bit of Haskell.

You can customize Xmobar by creating a file named .xmobarrc in your home folder with

gedit ~/.xmobarrc

and copy-paste the following example content

Config
{ bgColor =      "white"
, fgColor =      "black"
, border =       BottomB
, borderColor = "black"
, commands = -- what information to show
  -- network activity monitor (dynamic interface resolution)
  [ Run DynNetwork [] 10 -- network traffic
  , Run Memory ["-t" ,"RAM: <usedratio>%"] 10 -- RAM usage
  , Run Battery [] 10 -- battery time
  , Run Volume "default" "Master" [] 10
  , Run Date "%d %b %T" "mydate" 10
  , Run StdinReader -- text coming from xmonad
  ]
-- where to show command information
, template = "%StdinReader% }{ %memory% | %default:Master% | %dynnetwork% | %battery% | %mydate% |"
}

which you should modify to your liking. It uses a single Haskell (record) datastructure called Config where each field/option like bfColor or border is optional: If you don’t set a field, a default field value is used.
[How to create a Haskell datastructure?]

Most of the configuration options are self-explanatory. You can find a list of all options here.
How to check a Xmobar config file for syntax errors?

To check if your config file is correct or contains errors, run

xmobar <path to xmobar.hs>

in a terminal. Normally this is

xmobar ~/.xmobarrc
when your Xmobar config lies in your home folder.

The options that you surely want to tweak but may be difficult to understand are commands and template:
First, you define what information to show within commands. It’s a list of small apps (called “commands” or System Monitor Plugins) which run with a certain frequency to retrieve and summarize (system) information into a small piece of text. Each command defines a “template identifier” or “alias” like %mydate% or %memory%. Some commands define them automatically but for others you have to define them manually. For example, the Memory command defines the identifier %memory% automatically, while the Date command forces you to define an identifier like %mydate% yourself.
[How to write commands?]
[How to write lists in Haskell?]

Second, you define where to show the information of a command along the Xmobar statusbar with the template string. This is a string like

"%StdinReader% }{ %memory% | %default:Master% | %dynnetwork% | %battery% | %mydate% |"

which is printed literally into the statusbar after the “template identifiers” (like %mydate%) have been replaced by the piece of text from the corresponding command in commands. For example, the XMonad status text from the StdinReader command, which corresponds to %StdinReader%, will be printed on the left of the statusbar while the text from the Date command, which corresponds to %mydate%, will be printed on the far right.

Note that you can display a piece of text with color by wrapping it with “font color” HTML tags like

text <fc=#FF0000>red text</fc> other text

where the color is given in RGB hex notation.

Also note that the }{ symbol in template will separate everything on its left as far away as possible from everything on its right. If you like, you can configure/change this symbol (and the % symbol within the “template identifiers”) with:

Config
{ -- ...
, sepChar =  "%"   -- symbol to recognize template identfiers/aliases
, alignSep = "}{"  -- symbol to separate between left-center-right alignment
-- ...
}

How to write commands

Every command roughly follow this pattern:

Run <command name> [<options>] <refreshrate>
  • A command is named after its use, e.g. Wireless or Volume.
  • Every command has an “id”, “template identifier” or “alias” that is used in the template string to define where on in the bar the command’s output should be shown.
  • Every command has a list of options. If you leave the list empty, some default options are used. Possibly the most important option is "-t" (for “template”) to further tweak what and how information is actually shown. Take a look at the documentation of a command to find out what options are available.
    [How to write lists in Haskell?]
  • Every command has a refreshrate, which defines how frequently the command’s app is run. The unit for the refreshrate is a 10th of a second so that the default recommended refreshrate value of 10 updates your command every 1 second (divide the refreshrate value by 10 to get the number of seconds).
Show date and time

The abstract syntax for that is:

Run Date "<date formating>" "<template identifier>" <refreshrate>

For example, add the following line to your commands list:

Run Date "%d %b %T" "mydate" 10
Item Note
"%d %b %T Show day of month (%d), abbreviated month (%b) and hour-minutes-seconds (%T). You can heavily customize what to show and how..
"mydate" The identifier to be used in template. Choose any name you like that is not yet used.
10 The date and time status is checked and updated every 1 second.

To see the command’s output like

11 Oct 14:20:34

in Xmobar, add its identifier to template as follows:

template = ".... %mydate% .... "
Check out the Date docs.
Show wifi connection information

The abstract syntax for that is:

Run Wireless "<wifi device name>" [<options>] <refreshrate>

For example, add the following line to your commands list:

Run Wireless "wlp3s0" ["-t", "Wifi: [<qualitybar>] <essid>"] 10

You have to use your own wifi device name instead of "wlp3s0". Run iwconfig in a terminal to find your wifi device name. The output should look like this:

lo        no wireless extensions.

wlp3s0    IEEE 802.11  ESSID:"TP-LINK"... <-- contains your wifi device name

enp2s0    no wireless extensions.
Item Note
"-t", "..." How to output information. See options.
<qualitybar> To be replaced by ####:::: to represent the strength of your wireless connection.
<essid> To be replaced by the name of your wifi network, e.g. TP-LINK.
10 The wireless connection status is checked and updated every 1 second.

To see the command’s output like

Wifi: [####::::::] TP-LINK

in Xmobar, add its identifier to template as follows:

template = ".... %wlp3s0wi% .... "
Note: The wireless command automatically creates the template identifier like %wlp3s0wi%, which is “wi” after your wifi device name (%wireless% doesn’t work). So, theoretically you can run multiple wireless commands and show connection settings of multiple wireless devices.
Check out the Wireless docs.
Show audio volume

The abstract syntax for that is:

Run Volume "<soundcard" "<channel>" [<options>] <refreshrate>

For example, add the following line to your commands list:

Run Volume "default" "Master" ["-t", "Vol: [<volumebar>] <status>"] 10
Item Note
"default" The default soundcard to be used. Run alsamixer in your terminal to see a list of available soundcards.
"Master" The channel (a “device” in Alsa terminology) whose volume you want to output.
Run alsamixer in your terminal to see other channels. You may have to press F6 to try out other soundcards until you see Master.
However, Master affects the volumen of all other channels and is therefore the most useful.
"-t", "..." How to output information. See options.
<volumebar> To be replaced by ####:::: to represent your volume percentage.
<status> To be replaced by [on] or [off] depending on whether your sound is active or muted.
10 The volume status is checked and updated every 1 second.

To see the output like

Vol: [####::::::] [on]

of this command in Xmobar, add its identifier to template as follows:

template = ".... %default:Master% .... "
If you don’t see any volume output, you can use this workaround.
  1. Copy this script into a get-volume.sh file in your /home/user/.xmonad/ folder.
  2. Add the following line to your commands list:
Run Com ".xmonad/get-volume.sh" [] "myvolume" 10

Add its identifier to template as follows:

template = ".... %myvolume% .... "

Note: The volume command automatically creates the template identifier %soundcard:channel% whoose values you may have to look up using alsamixer (%volume% doesn’t work). So, theoretically you can run multiple volume commands for and show volumes of multiple channels from possible different soundcards.
Check out the Volume docs.

Show the screen brightness

The abstract syntax for that is:

Run Brightness [<options>] <refreshrate>

For example, add the following line to your commands list:

Run Brightness ["-t", "Brightness: [<bar>]"] 10
Item Note
"-t", "..." How to output information. See options..
If you can’t see any brightness status, you may have to set the --D option as in the workaround farther below.
<bar> To be replaced by ####:::: to represent your brightness percentage.
10 The brightness status is checked and updated every 1 second.

To see the output like

Brightness: [####::::::]

of this command in Xmobar, add its identifier to template as follows:

template = ".... %bright% .... "
If you don’t see any brightness output, you should try this workaround.

To get the brightness value, the Brightness command looks into the

/sys/class/backlight/

folder. There it expects a folder named acpi_video0. This folder usually contains files named actual_brightness and max_brightness, which contain the current and maximal brightness values of your screen. Depending on your PC or Notebook, this acpi_video0 folder may be called differently, e.g., intel_backlight or amdgpu_bl0. So that you have to modify the brightness command to take that into account:
If you have an Intel CPU+GPU, try

Run Brightness ["-t", "Screen: [<bar>]", "--", "-D", "intel_backlight"] 10

With the "-D" option, you change the name of the folder it expects. It has to come after the "-t" option and needs to be separated with "--", because it’s a “monitor-specific” option, which means that it’s an option that only exists for this Brightness command (in contrast to the "-t" option, which exists for all commands).

If you have an AMD Ryzen Mobile CPU+GPU, try

Run Brightness ["-t", "Screen: [<bar>]", "--", "-D", "amdgpu_bl0"] 10

If you don’t know your CPU or you folder may be called differently, run

cd /sys/class/backlight/ && ls

in your terminal app, to find out how the expected folder is named.

Note: The brightness command automatically creates the template identifier %bright% (%brightness% doesn’t work).
Check out the Brightness docs.

Run your own terminal apps/scripts

The abstract syntax for that is:

Run Com "<terminal app/script>" [<app arguments>] <template identifier> <refreshrate>

For example, add the following line to your commands list:

Run Com "whoami" [] "myuser" 0

to get the currently logged-in user or

Run Com "uname" ["-r"] "mykernel" 0

to get the current Linux kernel version.

Item Note
"whoami", uname The terminal app to run. You can run anything that you can run in your terminal, e.g. scripts.
[], ["-r"] App arguments. This will run whoami and uname -r in a terminal
"myuser", "mykernel" The identifier to be used in template. Choose any name you like that is not yet used.
0 The refreshrate. With 0 this command runs only once and is not updated again.

To see the output of these commands in Xmobar, add their identifiers to template as follows:

template = ".... %myuser% .... %mykernel% ..."
Check out the Com docs.
Tags: xmonad xmobar haskell tutorial status bar

Malte Neuss

Java Software Engineer by day, Haskell enthusiast by night.

Other Posts In Series

Just Enough Haskell For XMonad

Learn Haskell Functions with XMonad's XConfig example

Small changes to XMonad like changing keyboard shortcuts require little Haskell knowledge. However, bigger customizations, especially to organize your code when adapting other people’s configurations, are easier when you understand Haskell functions.

Read More

Just Enough Haskell For XMonad

Control the screen brightness in XMonad with Lux

The terminal app Lux allows you to set the screen brightness programmatically. You can use it from within XMonad as a workaround when FN function keys don’t work by default.

Read More

Just Enough Haskell For XMonad

Control the audio volume in Ubuntu+XMonad with Alsa and PulseAudio

The terminal app Alsa allows you to audio volume programmatically. You can use it from within XMonad as a workaround when FN function keys don’t work by default.

Read More