I got a TRMNL and DAMN it's nice!
My thoughts and my experience developing a TRMNL plugin.

Two months ago, on the 31st January, Snazzy Labs published a YouTube video showing the TRMNL - an E-Ink dashboard that's (mostly) fully open source and can show cool stats. I thought that this was a cool idea, so I bought one almost instantly. Apparently I wasn't the only one with that idea, though, because they were out of stock quite instantly.
But first... who or what is TRMNL?
TRMNL is an American company that produces E-Ink dashboards that act like a picture frame, but with helpful information. The interesting part is: Almost everything is open source. Oh, and the devices are assembled in America. And if you don't want to buy something that's just a microprocessor, a battery and an E-Ink display, you can build your own and use their servers (for a small one-time-pay fee) or simply host your own server.

After a lot of waiting and many updates in the newsletter and the Discord server, my TRMNL arrived a few days ago. The setup was really straightforward! Connect to the "TRMNL" Wi-Fi, enter Wi-Fi credentials, login to your TRMNL account and enter the "Friendly ID" to pair, that's it!
Trying it out before it arrived
Thanks to the Developer Edition, I got a virtual device to play around with the dashboard and some private plugins while waiting for my device to arrive. Okay, I didn't really do much with it, but... I could have!!

More info about the device and the package itself
I got the "Clarity Kit" add-on, which has the battery upgrade (4–6 months instead of 1–3), the developer edition, a charging cable plus a microfiber cleaning cloth and an anti-glare film.
The package is designed quite nicely. It even comes with a sim ejector tool! (Possibly for the reset button?) Oh, and there's a piece of paper that shows the initials of the person who assembled the device. Nice! 🙂 Besides that, the device was well packaged.
The device itself features an 800x480px E-Ink display, which displays the text and images really nicely.
How does it work?
The way the TRMNL works is actually quite simple. The actual content of the display gets pre-rendered in the Cloud. The device requests data and receives some JSON:
{
"status": 0,
"image_url": "https://example.org/path-to-img.bmp",
"filename": "2025-04-25T22:30:00",
"update_firmware": false,
"firmware_url": null,
"refresh_rate": "1800",
"reset_firmware": false
}
Example JSON generated by looking at the firmware
This JSON includes the image_url
, which is used to download the file and show it on the E-Ink display.
After that is done, the Wi-Fi connection and the file system both are getting shut down and the ESP inside goes to "deep_sleep" for refresh_rate
time.
When the sleep is done, the system boots up again and the cycle continues! :3
Building my first ever TRMNL plugin
On the day I got my TRMNL, I decided to make a Code::Stats plugin. After about 3 hours of tinkering, I got something I'm quite happy with.

TRMNL private plugins can have multiple ways of receiving data: Webhook, Polling and Static JSON data. The actual UI is just HTML code. TRMNL has made a design framework that makes stuff look great on an E-Ink display.

While developing the plugin, I found some things that might improve the plugin creation.
Improvement 1: Allow changing variables outside of HTML
As I had to work with JSON objects (Object.entries()
, sort()
, ...), I had to use JavaScript to add the machines and programming languages, as this isn't supported by Liquid. (Liquid is a markup language that is used by TRMNL.) This also meant I had to calculate the level in JavaScript too. Doing this results in the plugin currently not supporting multiple instances of itself.
I can't change the API of Code::Stats, so I'd love to be able to somehow change the data before the Markup is getting rendered. (Maybe with an additional JavaScript file that is executed before Liquid is?)
This idea might make private plugins more similar to native plugins. At the time of writing, I don't know how easy it is to make your own native plugins (and add them e.g. via merge request on GitHub?), but I'd love to be able to change the variables without needing an external server which could collect some usage data.
Improvement 2: Have a shared JavaScript file
Optimally, you need to create four views of your plugin: Full, Half horizontal, Half vertical and Quadrant.




All four views of my plugin.
As I had to write JavaScript to add all the data, I had to copy paste the whole JavaScript stuff to all four views. If I had to change something, I had to update all four views again. This is quite annoying, and I wish this could be done easier.
Okay, if Improvement 1 would be added, then this wouldn't be needed anymore, I guess.
Improvement 3: Better i18n
As you might have seen: The words "machines" and "top languages" are localized to German in the screenshots. That's because I added a helper function that translates the things.
const locale = `{{ trmnl.user.locale }}`;
const translations = {
en: {
Machines: "Machines",
"Top Languages": "Top Languages",
Level: "Level",
XP: "XP",
},
de: {
Machines: "Maschinen",
"Top Languages": "Top-Sprachen",
Level: "Level",
XP: "XP",
},
};
function t(key) {
const lang = locale in translations ? locale : "en";
return translations[lang][key] || key;
}
I wish there would be an official option to do this. Maybe even using Liquid or something.
This should also include stuff like "And 17 more" to be translated.
Bug 1: "Overflow" and "Fit Value" only work vertically
TRMNL's design system comes with some helpful tools, like Overflow and Fit Value. This can help when you don't know how long your data is and how much data you have.
The only issue is: This only works with height.
You want to use Fit Value because you don't have enough width? Good luck with that.
You want to make a list of items that's going from left to right? Don't expect the "And 17 more" thing to appear.
Bug 2: Markup preview takes long to update
It didn't happen a lot, but I found that sometimes, the markup preview did take quite long to reload. Sometimes like... 15 seconds or something. That's definitely not normal, I hope...
Conclusion
After all those improvement wishes and bug reports, it might sound like I don't like the TRMNL. That's not the case. I really like the idea, and I love that it's open source! It's definitely not perfect, but I think it's already great that you can create custom plugins.
I can definitely recommend buying one - if you want! Though, at the time of writing, they are still sold out, but this will probably improve in the next few months. I mean, I got mine eventually too!
For transparency: I was thinking of including a referral code that would allow you to get a few dollars off your TRMNL, but at the end I got a little panic when thinking about it (because I don't want my blog posts looking like they're sponsored, among other things). So, if you want to buy a TRMNL, then do it! If you want $15 off your order, you can watch the video of SnazzyLabs that I mentioned at the beginning of this post. In any way, you can still just look at the official website of TRMNL to see if it's something you like.
