Disclaimer: ChatGPT generated document.
A TUI (Terminal User Interface) is an interactive user interface rendered inside a terminal. It occupies the middle ground between a traditional command-line interface (CLI) and a graphical user interface (GUI): keyboard-centric, script-friendly and remote-friendly like a CLI, but with windows/panels, menus, tables, forms, dialogs, mouse support, colors, scrolling, and sometimes even graphics.
Modern TUIs have undergone something of a renaissance. Rust, Go, Python, C++, JavaScript, .NET, and other ecosystems now have sophisticated frameworks, while modern terminals support Unicode, 24-bit color, hyperlinks, mouse events, advanced keyboard protocols, and—in some terminals—actual images.
The distinction is useful:
| CLI | TUI | GUI | |
|---|---|---|---|
| Example | git status |
lazygit |
GitHub Desktop |
| Interaction | commands | keys/mouse/widgets | mouse/touch/widgets |
| Display | sequential text | screen-oriented | graphical windows |
| Rendering | stdout | terminal cells | pixels |
| Remote SSH | excellent | excellent | difficult |
| Automation | excellent | moderate | usually poor |
| Resource usage | tiny | low | higher |
| Discoverability | low–medium | medium–high | high |
A CLI usually behaves like:
$ git status
$ git add foo.rs
$ git commit
A TUI might instead display:
┌─ Files ──────────────┐┌─ Diff ───────────────────────┐
│ M src/main.rs ││ - old line │
│ A README.md ││ + new line │
│ ││ │
├─ Branches ───────────┤├─ Commits ──────────────────┤
│ * main ││ 31ac922 Fix parser │
│ feature-x ││ d02a811 Initial version │
└──────────────────────┘└─────────────────────────────┘
[Space] stage [c] commit [q] quit
But importantly, those boxes aren't graphical windows. They're usually Unicode characters painted into terminal cells.
At the lowest level, a terminal is essentially a grid:
column →
0 1 2 3 4 5 6 ...
┌───────────────
0│H e l l o
1│
2│
↓
row
Each cell may contain:
- a character
- foreground color
- background color
- attributes such as bold/italic/underline
- sometimes a wide Unicode glyph occupying two cells
Applications send control sequences mixed with text.
For example:
ESC [ 31 m
means roughly:
switch foreground to red.
And:
ESC [ 10 ; 20 H
means roughly:
move cursor to row 10, column 20.
These belong to the large family of ANSI/ECMA-48/DEC-style terminal control sequences.
A TUI renderer therefore does something conceptually like:
read input
↓
update application state
↓
calculate layout
↓
render widgets into virtual screen buffer
↓
compare buffer with previous frame
↓
send changed cells/control sequences
↓
terminal emulator displays them
That last optimization is important. Good frameworks don't necessarily repaint thousands of cells every frame.
TUIs are much older than the term itself.
Early computing involved physical terminals connected to mainframes and minicomputers.
There were printing terminals/teletypes:
computer → serial line → paper
followed by CRT "glass terminals."
A historically important family was DEC's VT series, particularly the VT100, introduced in 1978.
The VT100 helped popularize cursor positioning and standardized control sequences that heavily influenced today's terminal ecosystem.
Conceptually:
Teletype
↓
CRT terminals
↓
VT52 / VT100 / VT220 / ...
↓
ANSI/DEC control sequences
↓
Unix terminal abstraction
↓
xterm
↓
modern terminal emulators
Modern applications still speak protocols descended from machines that physically existed decades ago.
Writing escape sequences manually quickly became impractical because terminals differed.
Unix therefore developed abstraction systems such as:
termcap
↓
terminfo
Applications could ask:
How does this particular terminal move the cursor?
rather than hardcoding one terminal's escape sequence.
The famous curses library appeared in the late 1970s/early 1980s Unix ecosystem.
Today the major implementation is ncurses.
ncurses provides abstractions for:
- cursor movement
- screen updates
- windows
- pads
- colors
- keyboard input
- mouse input
- terminal capabilities
It remains enormously important. The current ncurses project continues to maintain the curses/terminfo ecosystem. (invisible-island.net)
Classic TUI programs built around curses-style APIs include things such as editors, file managers, installers, configuration tools and system monitors.
These concepts are frequently confused.
Suppose you open a terminal and run:
nvimThe stack might actually be:
┌──────────────────────────────┐
│ OS / desktop │
│ │
│ ┌──────────────────────────┐ │
│ │ Terminal emulator │ │
│ │ │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Shell: zsh │ │ │
│ │ │ │ │ │
│ │ │ $ nvim │ │ │
│ │ │ ↓ │ │ │
│ │ │ Neovim TUI │ │ │
│ │ └──────────────────────┘ │ │
│ └──────────────────────────┘ │
└──────────────────────────────┘
The terminal emulator is the graphical application displaying terminal cells.
The shell (bash, zsh, PowerShell, fish...) interprets commands.
The TUI application temporarily takes control of the terminal.
These are separate layers.
Common terminals include:
Linux / Unix
- xterm
- GNOME Terminal
- Konsole
- foot
- kitty
- Alacritty
- WezTerm
- Ghostty
macOS
- Terminal.app
- iTerm2
- kitty
- Alacritty
- WezTerm
- Ghostty
Windows
- Windows Terminal
- legacy Console Host
- ConPTY-backed terminals
- WezTerm
- Alacritty
- kitty
Windows Terminal documentation describes Windows Terminal as supporting Command Prompt, PowerShell, WSL and SSH profiles, along with tabs, panes, Unicode/UTF-8 and GPU-accelerated rendering. (Microsoft Learn)
WezTerm is particularly interesting because it's both a terminal emulator and multiplexer. It runs on Linux, macOS, Windows, FreeBSD and NetBSD and supports true color, hyperlinks, advanced text attributes, mouse reporting and several graphics protocols. (WezTerm)
And XTerm remains historically significant: its project describes it as the standard X Window System terminal emulator, with development going back to the 1980s. (XTerm)
This gets surprisingly complicated.
A program commonly sees:
echo $TERMwith values such as:
xterm
xterm-256color
screen
screen-256color
tmux-256color
linux
vt100
Libraries may consult the terminfo database to determine what that terminal claims to support.
But modern applications may additionally examine:
TERM
COLORTERM
TERM_PROGRAM
WT_SESSION
TMUX
SSH_TTY
...
or actively query terminal capabilities.
There is no single perfectly uniform "modern terminal standard."
Compatibility is instead a mixture of:
ECMA-48 / ANSI conventions
+
DEC VT behavior
+
xterm extensions
+
terminfo
+
terminal-specific extensions
Historically you had something like:
black
red
green
yellow
blue
magenta
cyan
white
Then:
Basic + bright variants.
Then:
Often referred to through:
TERM=xterm-256color
And modern terminals frequently provide:
24-bit RGB
≈ 16.7 million colors
For example:
ESC[38;2;255;100;40m
specifies an RGB foreground.
Current compatibility references show 24-bit color across many modern terminals including Ghostty, kitty, WezTerm, iTerm2 and Alacritty, although advanced features vary considerably beyond color. (Silvery)
Classic TUIs looked like:
+------------------+
| CPU: 52% |
+------------------+
Unicode allows:
╭──────────────────╮
│ CPU █████░░ 72% │
├──────────────────┤
│ RAM ███░░░░ 43% │
╰──────────────────╯
Useful characters include:
─ │ ┌ ┐ └ ┘
├ ┤ ┬ ┴ ┼
╭ ╮ ╰ ╯
█ ▇ ▆ ▅ ▄ ▃ ▂ ▁
⠁ ⠃ ⠇ ⠧ ⠷ ⠿
Braille is especially useful for high-resolution terminal charts because one character can represent multiple logical pixels.
Then there are symbols and emoji:
✓ ✗ ⚠ ● ◆ ▶
🐍 🦀 🚀
But Unicode creates hard problems: character width isn't universally trivial.
CJK characters may occupy two cells, combining marks may occupy zero additional cells, emoji sequences can involve multiple Unicode code points, and terminals don't always agree on rendering width.
This is one of the nastier engineering details of serious TUI frameworks.
A TUI typically puts the terminal into something resembling raw mode.
Normally terminal input is line-oriented:
hello<Enter>
Raw mode lets the program receive keys immediately:
h
e
l
l
o
Special keys often arrive as escape sequences.
For example, an arrow key might effectively arrive as bytes resembling:
ESC [ A
Frameworks decode these into events such as:
Key::Up
Key::Enter
Ctrl('c')
MouseDown(...)
Resize(...)
Paste(...)
Modern terminal protocols are improving keyboard disambiguation, including the Kitty keyboard protocol, but support isn't universal. Recent compatibility matrices show significant differences between terminals here. (Silvery)
Yes, TUIs can use mice.
Terminals can report:
click
release
drag
scroll wheel
coordinates
modifier keys
This enables:
- clickable buttons
- scroll bars
- selectable tables
- tabs
- text selection
- drag operations
Many modern TUI frameworks support mouse input, although keyboard-first interaction remains preferable because terminal applications are frequently used remotely.
This is one of the strangest modern developments.
Some terminals implement actual graphics protocols.
Major approaches include:
Sixel
An older bitmap protocol experiencing renewed use.
Kitty graphics protocol
Implemented by kitty and increasingly supported elsewhere.
iTerm2 inline images
An escape-sequence-based protocol originating in iTerm2.
Modern terminals such as WezTerm support combinations of iTerm2 images, Kitty graphics and Sixel. (WezTerm)
Consequently a "text interface" can now contain:
text
Unicode
24-bit color
images
animations
charts
hyperlinks
At that point the boundary between TUI and GUI gets surprisingly fuzzy.
This is the modern landscape.
Probably the best-known modern Rust TUI ecosystem.
Ratatui describes itself as a library for building "fast, lightweight, and rich terminal user interfaces." (Ratatui)
Typical architecture:
App state
↓
event loop
↓
Frame
↓
layout
↓
widgets
↓
terminal backend
Widgets include things like:
Block
Paragraph
List
Table
Tabs
Gauge
Chart
Scrollbar
Canvas
Rust is an especially good TUI language because it gives you:
- fast startup
- low memory usage
- native executables
- strong Unicode libraries
- concurrency
- excellent CLI ecosystem
- straightforward binary distribution
Bubble Tea is one of the most influential Go TUI frameworks.
Its architecture is based on The Elm Architecture:
Model
↓
Update(message)
↓
new Model
↓
View()
Conceptually:
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd)
func (m model) View() stringThis architecture works remarkably well for terminals because input naturally arrives as a stream of events/messages.
The Charm ecosystem around Bubble Tea also popularized libraries/tools such as Lip Gloss and Bubbles.
Go is attractive because:
single executable
fast startup
simple concurrency
easy cross-compilation
good networking ecosystem
Textual takes a substantially higher-level approach.
Think:
terminal + CSS-like styling + widgets + reactive application framework
You can build applications with:
- buttons
- trees
- tables
- inputs
- dialogs
- tabs
- scrolling containers
- reactive properties
- CSS-style layouts
- event/message systems
It feels more like building a web/desktop application than directly manipulating terminal cells.
Textual can therefore be excellent for:
internal tools
database clients
dashboards
admin consoles
developer tools
Python also has lower-level and simpler choices such as:
cursesurwidprompt_toolkitRichblessed
Rich isn't primarily a full-screen application framework, but it is extremely useful for styled terminal output, tables, progress displays and dashboards.
The historical foundation is:
curses / ncurses
For modern C++, one prominent option is:
FTXUI describes itself as a functional terminal user interface library and provides components, DOM-style composition and screen rendering. (GitHub)
Other interesting low-level systems include:
notcurses
PDCurses
termbox-style libraries
Notcurses is particularly ambitious about modern terminal capabilities such as Unicode, true color and multimedia.
A major framework is:
It provides a cross-platform terminal UI toolkit for .NET.
The programming model feels more like traditional desktop UI programming:
Window
Button
Label
TextField
ListView
Dialog
MenuBar
rather than manually drawing terminal cells.
Another widely used C# terminal ecosystem component is Spectre.Console, particularly good for polished CLI output and interactive prompts.
A particularly interesting approach is Ink.
Ink applies React concepts to terminals:
<Box>
<Text>Hello</Text>
</Box>So:
React component model
↓
terminal renderer
This can be attractive when a team already understands React and Node.js.
Other JS terminal UI projects have included:
blessed
neo-blessed
terminal-kit
Libraries include:
Lanterna
JLine
Lanterna offers higher-level terminal GUI abstractions, while JLine is especially important for sophisticated interactive command-line applications, REPLs and shells.
Even Bash can produce TUIs.
Common tools include:
dialog
whiptail
fzf
gum
For example, dialog can provide:
┌──────────────────────────┐
│ Choose environment │
│ │
│ ( ) Development │
│ (●) Staging │
│ ( ) Production │
│ │
│ < OK > │
└──────────────────────────┘
without writing a full UI framework.
There are TUI libraries for almost everything:
| Language | Libraries / frameworks |
|---|---|
| Rust | Ratatui, Cursive |
| Go | Bubble Tea, tview, termui |
| Python | Textual, Rich, urwid, prompt_toolkit, curses |
| C | ncurses, notcurses |
| C++ | FTXUI, ncurses |
| C#/.NET | Terminal.Gui, Spectre.Console |
| JS/TS | Ink, blessed, terminal-kit |
| Java | Lanterna, JLine |
| Ruby | TTY toolkit |
| Haskell | Brick |
| Zig | various emerging libraries |
| Shell | dialog, whiptail, gum, fzf |
Community discussion in 2026 particularly tends to cluster around Ratatui for Rust, Bubble Tea for Go, and Textual for Python, although the "best" choice depends strongly on language and application architecture. (Reddit)
There are far more than most people realize.
Vim
Neovim
Emacs (terminal mode)
Helix
Kakoune
top
htop
btop
glances
lazygit
gitui
tig
k9s
Midnight Commander
ranger
nnn
lf
yazi
tmux
GNU screen
Zellij
pgcli
mycli
lazysql
nmtui
bandwhich
gping
lazydocker
fzf
gh
although gh is primarily CLI-oriented rather than a traditional full-screen TUI.
Modern community discussions still routinely identify htop, tmux, and fzf as staples of the terminal ecosystem. (Reddit)
Suppose you run:
Ratatui app
↓
tmux
↓
SSH
↓
remote PTY
↓
local terminal
Now capability negotiation becomes tricky.
The application may see:
TERM=tmux-256color
rather than:
TERM=xterm-kitty
Some advanced escape sequences have to pass through tmux.
Thus:
terminal supports feature
does not necessarily imply
application can use feature
A current compatibility guide explicitly notes that multiplexers such as tmux, screen and Zellij can reduce the effective capabilities exposed by the underlying terminal. (FrankenTUI)
Consider a graphical administration application.
Remote usage might require:
VPN
remote desktop
browser
GUI forwarding
A TUI often needs only:
ssh server
k9sRendering happens locally in your terminal while the application executes remotely.
This makes TUIs exceptionally useful for:
- servers
- cloud infrastructure
- Kubernetes
- databases
- embedded Linux
- development environments
- remote machines
And explains why they never disappeared.
Modern TUI frameworks usually target some combination of:
Linux
macOS
Windows
BSD
Unix-like systems traditionally use pseudo-terminals:
PTY
Windows historically had a substantially different console API.
Modern Windows introduced ConPTY, greatly improving compatibility with Unix-style terminal applications and escape sequences.
This helped make applications such as:
Neovim
fzf
lazygit
Ratatui applications
Bubble Tea applications
much easier to support consistently across Windows and Unix-like systems.
But "cross-platform" still requires testing.
Most modern frameworks use immediate-mode rendering.
Instead of saying:
create window
modify label
move button
you often say:
render the entire desired UI from current state
Conceptually:
fn draw(frame: &mut Frame, app: &App) {
draw_sidebar(frame, app);
draw_editor(frame, app);
draw_statusbar(frame, app);
}The framework maintains buffers:
previous frame
↓
diff
↑
current frame
and emits only necessary terminal changes.
That is one reason terminal UIs can update smoothly despite their seemingly primitive output protocol.
A robust application frequently looks like:
┌──────────────┐
keyboard ──────────►│ │
mouse ─────────────►│ Event loop │
resize ────────────►│ │
network ───────────►│ │
└──────┬───────┘
↓
┌──────────────┐
│ App state │
└──────┬───────┘
↓
┌──────────────┐
│ UI renderer │
└──────┬───────┘
↓
┌──────────────┐
│ Cell buffer │
└──────┬───────┘
↓
terminal
This naturally resembles:
- Elm
- Redux
- Model-View-Update
- unidirectional state architectures
which explains why Bubble Tea's Elm-inspired design and Ratatui's state/render separation work so well.
Modern frameworks often supply:
Paragraph
Label
Button
Input
TextArea
List
Tree
Table
Tabs
Menu
Popup
Dialog
ProgressBar
Gauge
Sparkline
Chart
Scrollbar
Canvas
But unlike GUI widgets, they're eventually reduced to terminal cells.
A progress bar:
████████████░░░░░░░░ 60%
A sparkline:
▁▂▃▅▄▇█▆▄▃
A tree:
src/
├── app.rs
├── main.rs
└── ui/
├── editor.rs
└── sidebar.rs
This is essentially ASCII/Unicode graphics.
Modern frameworks often support constraints resembling GUI layout engines:
vertical
├── header: 3 rows
├── content: remaining
└── footer: 1 row
Then:
content
├── sidebar: 25%
└── editor: 75%
Some frameworks offer:
- flex layouts
- percentages
- minimum/maximum dimensions
- fixed dimensions
- nested containers
Textual goes particularly far toward web-style layout concepts.
A terminal might be:
80×24
or:
240×70
or a tiny SSH pane:
40×12
Good TUIs therefore adapt.
Large:
┌─ Files ──────┐┌─ Preview ──────────────────┐
│ ... ││ ... │
└──────────────┘└────────────────────────────┘
Small:
┌─ Files ──────────────┐
│ ... │
└──────────────────────┘
Exactly the same concept as responsive web design, except measured in character cells.
Modern TUI developers eventually encounter acronyms like:
CSI
OSC
SGR
DCS
DECSET
DECRST
Some important capabilities include:
SGR
Text styling:
bold
italic
underline
foreground
background
Alternate screen
Lets full-screen applications take over the terminal and restore the previous shell display afterward.
Bracketed paste
Lets the program distinguish typed input from pasted text.
Mouse tracking
Reports mouse activity.
Focus reporting
Tells applications whether the terminal gained/lost focus.
OSC 8
Clickable hyperlinks.
OSC 52
Clipboard interaction.
Synchronized output
Allows terminals to avoid showing partially rendered frames.
Kitty keyboard protocol
Improves keyboard event representation.
Kitty/Sixel/iTerm graphics
Image rendering.
Current support differs considerably by terminal: basic colors are nearly universal, whereas synchronized output, Kitty keyboard, graphics, hyperlinks and clipboard-related capabilities are much less uniform. (Silvery)
A naïve program might repeatedly:
clear screen
draw everything
clear screen
draw everything
which causes flickering.
Modern renderers instead:
Frame A
↓
Frame B
↓
calculate changed cells
↓
update only differences
and may use synchronized output when supported.
This produces much smoother interfaces.
Terminal rendering has unusual bottlenecks.
Usually the expensive part isn't:
calculating the UI
but:
generating escape sequences
+
writing them through PTY
+
terminal parsing
+
glyph rendering
Especially over:
SSH
serial connections
slow multiplexers
So good libraries minimize output.
A terminal app doing:
60 FPS × 200 × 60 cells
can generate a surprising amount of data if implemented badly.
TUIs have both strengths and weaknesses.
Strengths include:
- entirely keyboard operable
- usable without precise pointing
- excellent zoom via terminal font scaling
- potentially screen-reader compatible
- high-contrast themes
Problems include:
- heavy Unicode/icon use
- color-only information
- unconventional keyboard navigation
- custom widgets
- screen readers struggling with constantly redrawn screens
Accessible TUI design should avoid making color or exotic glyphs the only representation of meaning.
TUIs are especially attractive when you want:
Low resource consumption
A sophisticated interface may consume a fraction of a browser/Electron application's resources.
Remote execution
SSH is native territory.
Fast startup
Native Rust/Go/C/C++ applications can feel essentially instantaneous.
Keyboard efficiency
Expert users can operate them extremely quickly.
Cross-platform distribution
Especially with Rust and Go binaries.
Automation coexistence
A project can expose both:
mytool deploy --prod
and:
mytool
for an interactive interface.
Developer audience
Developers already live in terminals.
Terminals impose genuine constraints.
You get cells, not arbitrary pixels.
No normal GUI control over fonts.
Glyph width and emoji rendering can differ.
Historical terminal protocols weren't designed for today's keyboards.
One terminal supports Kitty graphics; another Sixel; another neither.
Terminal ↔ remote application ↔ SSH ↔ multiplexer ↔ OS clipboard is complicated.
Full-screen dynamically redrawn interfaces aren't automatically accessible.
Keyboard shortcuts must be learned.
Terminal applications can technically work on phones but usually aren't optimized for touch.
TUIs are excellent for:
developer tools
DevOps tools
Kubernetes clients
Git clients
database browsers
server administration
log viewers
system monitoring
file managers
SSH tools
build systems
interactive installers
debugging tools
networking tools
AI coding tools
They're less suitable for:
photo editing
video editing
CAD
consumer mobile applications
highly visual design
complex drag-and-drop workflows
maps
graphics-heavy applications
A useful practical map is:
| Situation | Strong candidate |
|---|---|
| Want maximum native performance | Rust + Ratatui |
| Building cloud/DevOps tooling | Go + Bubble Tea |
| Fast development/internal tool | Python + Textual |
| Existing Python CLI | Rich / Textual / prompt_toolkit |
| C++ project | FTXUI |
| Existing .NET application | Terminal.Gui / Spectre.Console |
| React/Node team | Ink |
| Low-level Unix application | ncurses/notcurses |
| Simple shell workflow | gum/dialog/fzf |
The three especially influential modern choices are:
Ratatui for Rust Bubble Tea for Go Textual for Python
Their philosophies are noticeably different:
Ratatui
state → frame → widgets → cells
Bubble Tea
model → message → update → view
Textual
application → widgets → events → CSS/layout
You can think of the whole stack as:
┌───────────────────────────────────────────────┐
│ TUI APP │
│ lazygit / k9s / htop / your app │
├───────────────────────────────────────────────┤
│ TUI FRAMEWORK │
│ Ratatui / Bubble Tea / Textual / FTXUI / ... │
├───────────────────────────────────────────────┤
│ TERMINAL ABSTRACTION LAYER │
│ curses / crossterm / terminfo / raw mode │
├───────────────────────────────────────────────┤
│ TERMINAL PROTOCOLS │
│ ANSI / ECMA-48 / DEC / xterm / OSC / CSI │
├───────────────────────────────────────────────┤
│ PTY / ConPTY / SSH │
├───────────────────────────────────────────────┤
│ TERMINAL EMULATOR │
│ kitty / WezTerm / xterm / iTerm2 / WT / ... │
├───────────────────────────────────────────────┤
│ GUI / OS │
│ Linux / macOS / Windows / BSD │
└───────────────────────────────────────────────┘
Sometimes additional layers appear:
TUI
↓
tmux
↓
SSH
↓
PTY
↓
terminal
which is why terminal compatibility is much more complicated than "does ANSI work?"
The terminal used to be constrained by:
monochrome
ASCII
slow serial links
limited keyboards
physical CRT hardware
Today's terminal can provide:
Unicode
24-bit RGB
emoji
mouse
clipboard
hyperlinks
images
GPU rendering
fast local PTYs
SSH
modern keyboard protocols
Meanwhile languages like Rust and Go make it easy to distribute fast, self-contained developer tools.
That's a powerful combination.
The terminal has therefore evolved from:
dumb text display
into something closer to:
portable interactive rendering protocol
while retaining decades of backwards compatibility.
That backward compatibility is simultaneously the terminal's superpower and biggest engineering headache.
A program designed around relatively conservative VT/xterm behavior can work through SSH on an enormous variety of machines. But exploiting modern features—graphics, richer keyboard handling, clipboard access, synchronized rendering—requires capability detection and fallbacks because support still differs significantly among terminals. (Silvery)
And that's perhaps the most interesting thing about TUIs: a modern Ratatui/Textual/Bubble Tea application running in a GPU-accelerated terminal is ultimately descended from software protocols created for physical CRT terminals connected by serial cables in the 1970s. Yet the model remains useful enough that developers are still building new frameworks, terminal emulators and applications around it nearly half a century later.
