cloudparts

Terminal user interfaces (TUIs)

Interactive programs that draw their whole interface as text in a terminal window, with panels, lists and keyboard control, and the libraries used to build them.

3 technologies on this page ↓

How it works

A terminal is a grid of character cells. A program draws on it by printing text mixed with escape sequences: byte strings that start with the ESC character and move the cursor, set colors or clear part of the screen, a convention standardized as ECMA-48 and ANSI X3.64 and spread by DEC’s VT100. A full-screen TUI first puts the terminal into raw mode (with termios on Unix), so each keypress arrives at once, unechoed and without line editing. It then usually switches to the alternate screen buffer (CSI ? 1049 h in xterm): a second screen exactly the size of the window, with no scrollback, that the app can repaint freely without disturbing the shell that started it. On exit it switches back and the earlier output reappears. Keypresses, and mouse clicks, which xterm-style terminals also report as escape sequences, feed an event loop that updates the app’s state and redraws. Repainting every cell on each change sends more characters and flickers, so libraries keep two copies of the screen, what is shown and what should be, and send only the difference: curses calls them the physical and virtual screens, and Ratatui renders each frame into a buffer and diffs it against the previous one. Libraries differ most in how you describe the interface. Ratatui is immediate mode: your code redraws the whole UI from application state every frame. Bubble Tea follows the Elm Architecture: a model, an Update function that turns each message into a new model, and a View that renders it. Textual keeps a tree of widgets, which it calls the DOM, styled with its own CSS, and the same app can run in a browser. Differences between terminals are looked up in terminfo, a database describing thousands of them, and Windows consoles interpret the same VT sequences once a program enables ENABLE_VIRTUAL_TERMINAL_PROCESSING.

Think of it as

A split-flap departure board. It’s a fixed grid of letter tiles; to show a new train it flips only the tiles that change, not the whole board, and it can be read from anywhere the board hangs.

Reach for it when

  • Tools people use where they already work: in a shell, inside a container, or on a server over SSH, such as htop, lazygit or k9s
  • Keyboard-driven dashboards and browsers over data that changes: processes, logs, queues, Git history, cluster resources
  • Developer tools that need more than a prompt but not a desktop app

Think twice when

  • Users who don’t live in a terminal, or interfaces that need images, fine-grained charts or drag and drop
  • Output other programs will read: a TUI’s screen is escape sequences, so also offer a plain-text or JSON mode for scripts and pipes

A real example

A developer keeps lazygit open in a terminal split beside the editor. Staged files, branches and the commit log are panels; a keystroke stages a file, and the library rewrites only the few cells that changed rather than all 4,000 or so on a 160 × 26 screen. The same works in an SSH session on a build server. Quitting puts the terminal back in normal mode and switches off the alternate screen, so the shell’s scrollback is exactly as it was.

Technologies

Specific products, and what each does differently.

Libraries and how they model the screen#

Every TUI library ends up writing escape sequences to the same character grid; they differ in how your code describes what should be on it.

LibraryLanguageHow you describe the UI
curses / ncursesC, with bindings for many languagesWrite into windows, then call refresh(); curses diffs a virtual screen against the physical one
RatatuiRustImmediate mode: redraw the whole UI from state every frame into a buffer, which is diffed against the previous frame
Bubble TeaGoThe Elm Architecture: a model, Update for each message, View to render; inline or full-window
TextualPythonA tree of widgets (a DOM) styled with CSS, asynchronous underneath; also runs in a web browser
InkJavaScript (Node.js)React components rendered to the terminal

In code · bash

# What a TUI library does under the hood, by hand in a shell
stty raw -echo                     # keys arrive one at a time, not echoed
printf '\033[?1049h'               # switch to the alternate screen
printf '\033[2J\033[H'             # clear it, cursor to row 1, column 1
printf '\033[1;32mDeploys\033[0m'   # bold green text, then reset
printf '\033[3;1H> api   ok'        # draw at row 3, column 1
printf '\033[4;1H  web   failed'    # and row 4
key=$(dd bs=1 count=1 2>/dev/null)  # wait for one keypress
printf '\033[?1049l'               # back to the normal screen, scrollback intact
stty sane                          # restore echo and line editing

Worth exploring

WebsiteAwesome TUIsJustin Garrison and contributors, on GitHubA community-maintained list of more than 700 terminal apps and libraries in 13 sections, from dashboards, editors and Docker or Kubernetes front ends to games, plus TUI libraries grouped by language (Python, Go, C, C++, Java, .NET, Rust and others). Entries must be interactive, maintained and not just wrappers around tools like fzf; they arrive by pull request with one-line descriptions, and the maintainer says to run them at your own risk.
All resources →

Often confused with

Where it came from1976–2023

Early video terminals each had their own control codes; a VT52 moved the cursor with one sequence and a Hazeltine 1500 with another, so programs relied on libraries such as termcap to speak to any of them. The ECMA-48 standard of 1976 and ANSI X3.64 of 1979 defined one set of escape sequences, and DEC’s VT100, introduced in August 1978, was one of the first terminals to support them. It sold so well that its codes became the de facto standard for hardware terminals and, later, for terminal emulators.

Full-screen programs needed a layer that hid the remaining differences. At the University of California, Berkeley, students wrote the screen handling for the vi editor on top of the termcap database, then turned those routines into the curses library, first published in 4.0BSD in late 1980. One of them later built terminfo at Bell Labs, released with System V Release 2 in early 1984, and a rewritten curses followed in 1986. Pavel Curtis’s free pcurses became the base of ncurses, whose first widely used version, 1.8.1, appeared in November 1993; ncurses is still maintained today.

A new generation of libraries borrowed ideas from games and the web. Florian Dehau’s tui-rs brought an immediate-mode design to Rust; it is no longer maintained, and in 2023 it was forked as Ratatui to continue its development. Charm’s Bubble Tea applied the Elm Architecture to Go, and Will McGugan’s Textual gave Python a widget tree styled with CSS that can also run in a browser. Justin Garrison’s awesome-tuis list now catalogs more than 700 terminal apps and libraries, from system monitors to Kubernetes dashboards.

Why it’s built this way

Escape sequences in the same byte stream as the text
No second channel is needed: anything that can print can move the cursor or change color, so the same app works over a 1978 serial line or a modern SSH connection.
Describe terminals in a database instead of hard-coding them
termcap and then terminfo let one program run on thousands of terminal types: curses looks up each terminal’s capabilities and emits the right codes.
Send only what changed
curses keeps a virtual and a physical screen and transmits the difference; batching several windows into one update sends fewer characters and “avoids a visually annoying flicker”. Ratatui uses the same double-buffer idea.
Borrow architectures from games and the web
Immediate mode ties the screen directly to state, with no widget objects to keep in sync; the Elm Architecture funnels every event through one Update; Textual’s DOM and CSS let web developers reuse what they know.

Timeline

  1. 1976

    ECMA-48 standardizes terminal control sequences

  2. 1978

    DEC introduces the VT100 (August)

  3. 1980

    The curses library is published in 4.0BSD

  4. 1984

    terminfo ships with System V Release 2

  5. 1993

    ncurses 1.8.1, the first widely used version (November)

  6. 2023

    Ratatui forks tui-rs to keep it developed

Sources12

Improve this page on GitHub