upvalue.io
now | posts | contact

A minimal TRMNL setup to display your own stuff

  • Motivation
  • How to do it
  • What to use to publish the endpoint
  • Example code
  • Relevant TRMNL docs
PUBLISHED
August 9, 2025
PREVIOUS Making Claude Code into an autonomous frontend dev
NEXT Trialing Zig and Rust with a Tcl interpreter
picture of trmnl displaying hello world

#Motivation

Recently I got my hands on a TRMNL, an “e-ink dashboard to stay focused.” Essentially it’s an e-ink screen that on a cadence that you set pulls some content from somewhere else.

TRMNL also has a plugin marketplace that allows you to install plugins others have made (and of course share with them).

For my part I was mostly interested in putting my own stuff on the screen without publishing a plugin to the marketplace and wanted to find the most expedient way to do that. It wasn’t immediately obvious to me how to do that as it was also my first time using TRMNL, so I wrote it down here.

Even if you aren’t using others’ plugins, “plugins” is the abstraction you’ll need to use for this unless you go with the bring your own screen option.

#How to do it

To do this you’ll need some way to either expose some code on the internet at a URL TRMNL can access, or run some code on a schedule so you can push to TRMNL’s webhook interface.

As far as I can tell the simplest way to do this is to use the TRMNL “Private Plugin.” From the TRMNL.com plugins page, you want to add a new private plugin.

TRMNL plugin configuration example showing markup editor

On the plugin configuration page you can then point your new private plugin instance at the code you’ve written. You can select either polling or webhook; I chose polling as it’s slightly easier to set up.

Paste in an endpoint of yours that will return some application/json. For starters, you can just return {"Text": "Hello World"}. On the plugin page, edit the markup to display this text. Here’s a minimal page that will display the text you return.

When setting this up for the first time, you’ll probably need to “force refresh” both the plugin and your TRMNL itself (by pressing the button on the back). The debug logs on the page are also helpful.

The polling headers are a good spot to set up some authorization if you’ll be exposing any sensitive information from your endpoint.

#What to use to publish the endpoint

If you don’t have a place to host code, I found fly.io to be pretty painless. One thing to bear in mind is that TRMNL will time out polling if your code doesn’t respond in time, so if you are using a scale-to-zero solution like Fly, it’s important to make sure you can boot your code and render within a few seconds. Webhooks may be better for a more complex setup.

#Example code

Here’s my minimal setup for Fly. Making the plugin useful is left as an exercise for the reader.

#Relevant TRMNL docs

  • Design system for guidance on how to write the markup

  • How to set up a new device

  • Screen templating

  • Private plugins

  • Bring your own screen. If you want to bypass using TRMNL’s SaaS entirely, you can do this. TRMNL helpfully publishes a few templates for doing this with different stacks.

    For my use case it didn’t seem necessary just yet, but one advantage of doing this is that you can tweak parameters outside of what TRMNL recommends. For example, a cursory glance over the firmware code seems to suggest that a BYOS setup could set a more aggressive refresh rate than the fastest allowed for a TRMNL plugin.

PREVIOUS Making Claude Code into an autonomous frontend dev
NEXT Trialing Zig and Rust with a Tcl interpreter
now / posts / contact
GitHub