DEV Community


Posted on



Create your i3 status bars in Node.js

What is i3?

i3 is a tiling window manager available for GNU/Linux and Unix operating systems. A tiling window manager in a nutshell helps your organize your window by arranging them equally on the screen. If you have one window, it will take all the space available, two window and it will split the space available in two to arrange them, etc...

an example of my desktop running i3

Screenshot of my desktop running VIM (top) and Konsole (bottom).

What is i3status

i3status is a status bar for the i3 window manager. The idea is similar to what is found on the Mac OS X operating system: display some status about your operating system, applications, events, stuff like that. I have never had an OS X operating system before so I can't say whether it is easily customizable or not. But the i3status has that advantage: you want to show some third party program output from an API? You got it! You want to have the weather for your location? i3status got you covered as it is highly customizable.

The status bar I use everyday with the help of @i3bar/core.

Some facts

@i3bar/core is the NPM package I released recently. What's great about i3 window manager is that it even let you replace the i3status default bar with your own, and by your own, I mean whether the one you liked & installed from GitHub or your very own written in the language you want as every i3 status bar is following the i3 protocol. The protocol tells you what it wants from you in the end and you have to provide it in some way no matter what. That information is in fact a JSON object that list all blocks that will be displayed on the status bar. Blocks are just JSON objects that contains properties such as the full_text property that will contain the block output (like 71% for the battery).

In the end, we could write our own Node.js status bar with a few lines of code like so:

"use strict";

const sleep = seconds => new Promise(resolve => setTimeout(resolve, seconds * 1000));

async function loop() {
  console.log(JSON.stringify({ full_text: new Date().toISOString() }));
  // {full_text: "2019-08-13T19:47:18.743Z"}

  await sleep(5);
  // wait five seconds, and then update the date

Enter fullscreen mode Exit fullscreen mode

Basic implementation to understand how the i3 protocol works

So, knowing that I could write my own bar, I naturally wrote it in... PHP. Yes, I like PHP and I had a great time writing this bar in PHP. Except the bar was pretty static in the sense that it did not react to events such as a click or a volume scroll. In fact, you are in the obligation to provide a JSON array of all blocks-objects you want to display, but in the same time, it sends you JSON as well for the events. And I had a really hard time integrating a non-blocking I/O model on my script to get the events working. And even after that, the bar was not responding smoothly enough and I knew I had to choose another language. Now don't get me wrong, PHP is great, in fact, I am working with PHP everyday, but sometimes, you gotta choose the right tool for the job, and the right tool was...

Node.js to the rescue

So then I re-wrote the thing in JavaScript for Node.js. As I had gained some experience about the i3 protocol, I knew it was gonna be easy to translate my PHP code into JavaScript. Not only I did that, but I also found some performance gains and I really made my day knowing that I had built something that would allow me to finally write my blocks the way I want, with the language that I love and know pretty well know.

My own bar written in JavaScript

What did I learn

In the end, I don't even think that Node.js is the perfect language to write i3 status bars, but it is hell of a joy to write it in your favorite language and be able to customize your operating system that way. I also used some community package for i3 status, such as i3status-rs which is written in Rust. I loved the way it looked, but I really wanted to learn more about this protocol, and also see what I would came up with. Also, Rust is an amazing language, but the time to compile the bar was too long to me (too many third party packages I guess), and in the end, choosing an interpreted language that is event driven like JavaScript in Node.js is pretty clever to handle that use case (be able to update your bar by issueing events).

Source-code from the i3status-rs repository

What to do next?

I plan on adding some more features such as the coordinates position of where you clicked on a block (seems too much to me, but the protocol allows this kind of information, and maybe some people will come up with ideas I had not in the beginning). I also want to create another package called @i3bar/blocks where you will be able to pick a block, customize it (instead of written the whole thing, more fun for me), and use it in your configuration instead of having to write everything. Because in the end, @i3bar/core is just an SDK, but there is a example folder in the repository so that you can check out how I made the status bar I use everyday now. And maybe some utilities for defering operations like fetching informations from an api and refreshing it every 10 minutes (I'm looking at you, openweathermap). This project is just the beginning and I hope I have the support of the community to help me provide some more features!

Thanks for the time you took to read, if you have any question, if I have made a typo or a mistake, do not hesitate to ask. Do not hesitate to open an issue on GitHub, to discuss something that you did not understand or for a bug/feature.

Knowing that I am sure not the best Node.js or even JavaScript developper, I am counting on you to guide me through the path of perfection and if you see some things that bother you about my code, we can even discuss them here or in an issue.

Have a great day and don't stop being curious!

Top comments (0)


11 Tips That Make You a Better Typescript Programmer

1 Think in {Set}

Type is an everyday concept to programmers, but it’s surprisingly difficult to define it succinctly. I find it helpful to use Set as a conceptual model instead.

#2 Understand declared type and narrowed type

One extremely powerful typescript feature is automatic type narrowing based on control flow. This means a variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type.

#3 Use discriminated union instead of optional fields


Read the whole post now!