Ansi.md

The TUI

Most CLI apps run for a moment, write some output, and then exit. Think ls. But there's a category of full-blown apps that hang around so you can interact with them. This kind of app is called a Terminal User Interface, or TUI for short. A broad definition would technically include stalwarts like emacs or tmux, but here we are mainly focused on newer TUIs that strive for beautiful design with advanced layouts.

Disclaimer - I've only written a handful of TUI apps, mostly unreleased. My favorite is probably my silly little RPN calculator vectro, built with bubbletea. Also on web at vectro.app, which nobody asked for. It's not that exciting, please ignore.

Gratuitous Screenshots

Let your imagination run wild, we can do a lot these days:

Ecosystem

If you think about it, each TUI must include the crude equivalent of a web browser - event loop, layout engine, and widgets. TUI libs are powerful and complicated. Some of them are like react, with nested components and props. Some use flexbox. Let's start by checking out some popular TUI apps. I award a highly-coveted chicken emoji to apps I've used and enjoyed. 🐔

applang⭐ starstui libwhat
fx 🐔go21kbubbleteajson viewer
gh-dashgo12kbubbleteagh dashboard
slidesgo12kbubbleteaslides
superfilego23kbubbleteafile manager
memraypy15ktextualpy memory prof
postingpy12ktextuallike postman
bandwhich 🐔rs12kratatuibandwidth monitor
csvlensrs4kratatuicsv viewer
elio 🐔rs1kratatuifile manager
gituirs22kratatuigit
llmfit 🐔rs31kratatuilocal LLM picker
presentermrs9kratatuislides
spotify-playerrs7kratatuispotify
trippy 🐔rs7kratatuiping
yazi 🐔rs41kratatuifile manager
gloomberbts1kopentuifinance
hunk 🐔ts8kopentuidiff viewer
btopc++33kcustomtop
glancespy33kncursestop
goaccessc21kncurseslog file viewer
k9sgo34ktviewkubernetes
lazydockergo52kgocuidocker
lazygit 🐔go81ktcellgit
nnnc22kncursesfile manager
spotify-tuirs19ktui-rsspotify
tig 🐔c13kncursesgit

And let's not forget the ai harnesses:

applang⭐ starstui lib
claude-code 🐔ts140kink
codex 🐔rs91kratatui
copilot-clits11kink
gemini-clits106kink
hermes-agentpy/ts222kink
opencodets191kopentui
pi 🐔ts90kcustom

TUI Frameworks

Again, I haven't used all of these so I can't really provide a lot of guidance. The only one I've shipped with is bubbletea, which I found to be fast and productive. Personally I would avoid writing complicated TUIs in typescript. Use something native like go/rust, and probably bubbletea/ratatui if you have a choice.

liblang / cloc⭐ starsdesignlayout
bubbletea 🐔go / 24k44kmodel/update/viewlipgloss / manual
inkts / 54k40kreactflexbox
opentuits / 93k13kreact/solidflexbox
ratatui 🐔rust / 21k22kimmediate modeconstraints
textualpython / 46k37kreact-likecss-like

Bubbletea cloc includes deps like Ultraviolet, Ink includes React, Textual is ts+zig. These frameworks are beasts.

Rendering

TUI frameworks have an interesting set of constraints. In general, the bottleneck is not widgets or layout - it's pushing display commands (ansi escape codes) to the terminal. When I was working on my ansi movie player, I could run at thousands of fps as long as I was targeting /dev/null. Inside a real terminal, that abruptly dropped to 25 fps. So how do these TUIs work?

  • bubbletea / opentui / ratatui - On each render, these frameworks rebuild the complete frame and diff against the previous frame. They emit only the ansi commands needed to bring the terminal up to date, with optimizations to keep that output small. The bottleneck is the terminal.

  • ink - My understanding is that this thing wasn't built for speed, and does full frame re-renders by default. The major harnesses use custom rendering to calculate diffs like the other frameworks.

  • textual - Textual tracks dirty rectangles, much like a traditional windowing system. Probably overkill for terminals, an approach that's largely been rejected by other TUI frameworks.