Bubble Tea

A Go framework based on The Elm Architecture for building terminal UIs, from simple prompts to full-window, high-performance TUI applications.

Framework
Go
vv1.3.10
45,488 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
85 /100 Excellent
Development Activity 84
Maintenance 88
Community 68
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
84 /100 Excellent
Architecture 88
Code Quality 78
Innovation 85
Learning Curve 85

Bubble Tea is a framework from Charm for building terminal user interfaces in Go. It applies The Elm Architecture — a single Model holding application state, an Update function that reacts to incoming messages, and a View function that renders the UI — giving terminal apps the same predictable, unidirectional data flow popularized by Elm and Redux.

The framework ships a high-performance, cell-based renderer with built-in color downsampling, declarative view composition through layered View values, and high-fidelity keyboard, mouse, and paste handling, alongside native clipboard support. Programs can run inline, in full-window alt-screen mode, or mix both, and Bubble Tea handles terminal setup, raw-mode input, and redraw scheduling so application code only has to describe state transitions and rendering.

Bubble Tea is battle-tested in production and anchors the wider Charm ecosystem: Bubbles supplies ready-made components (text inputs, viewports, spinners), Lip Gloss handles styling and layout, and Harmonica adds spring-based animation. Thousands of published tools — from dotfile managers to Hacker News readers to Tetris clones — are built on top of it.

What You Get

  • A Model interface (Init, Update, View) that structures any TUI as a pure state machine
  • A cell-based renderer with automatic ANSI color downsampling for the terminal’s actual color profile
  • High-fidelity keyboard, mouse, and bracketed-paste event handling via KeyPressMsg, MouseMsg, and paste messages
  • Native OS clipboard read/write support without shelling out to external tools
  • Composable View layers, including per-view mouse handlers and cursor, window-title, and progress-bar control
  • ProgramOption functions (WithContext, WithInput, WithOutput, WithEnvironment) for embedding Bubble Tea in remote sessions like SSH

Common Use Cases

  • Building full-window TUI dashboards and admin tools that run over SSH or locally
  • Writing interactive CLI wizards and forms (multi-step prompts, selection lists, text inputs)
  • Creating developer tools like chezmoi and gh-dash that need a rich, stateful terminal UI
  • Building terminal games and visualizations (Tetris clones, cellular automata, ASCII animations)
  • Adding an inline, non-full-screen interactive component to an otherwise scripted CLI

Under The Hood

Architecture

Bubble Tea’s public API centers on tea.go, which defines the Model interface (Init/Update/View) and the Program type that drives the event loop. commands.go and key.go/mouse.go layer typed message handling on top, while options.go exposes a functional-options constructor (WithContext, WithInput, WithOutput, WithEnvironment) so NewProgram’s signature stays stable as capabilities grow. Beneath this public layer sits a platform-abstraction layer split by build tags — tty_unix.go/tty_windows.go, termios_bsd.go/termios_unix.go/termios_windows.go/termios_other.go, and signals_unix.go/signals_windows.go — isolating raw-mode terminal setup and signal handling per OS. Core event and rendering primitives (Msg is a direct type alias for uv.Event) are delegated to the sibling charmbracelet/ultraviolet module rather than reimplemented, and renderer.go/cursed_renderer.go build the cell-based diffing renderer on top of that. The Model interface is the single contract every downstream consumer (Bubbles components, the wider Charm ecosystem, and thousands of dependent applications) is built against, so it is the one abstraction that cannot change without a major version bump — which the repo has in fact done for v2.

Tech Stack

The module (charm.land/bubbletea/v2, Go 1.25) depends on charmbracelet/colorprofile for terminal color-profile detection, charmbracelet/ultraviolet for core terminal event/rendering primitives, charmbracelet/x/ansi and charmbracelet/x/term for low-level ANSI/terminal handling, lucasb-eyer/go-colorful for color math, and muesli/cancelreader for cancellable stdin reads. Indirect dependencies include aymanbagabas/go-udiff (render diffing), mattn/go-runewidth and rivo/uniseg (Unicode width/segmentation for correct cell rendering), and charmbracelet/x/termios. The project builds via a Taskfile.yaml (the Task runner) rather than a Makefile, and CI is a set of dedicated GitHub Actions workflows — separate build, lint, coverage, and examples pipelines plus Dependabot and a lint-sync workflow — rather than one monolithic pipeline.

Code Quality

Seven _test.go files cover the core (tea_test.go, 18 test functions exercising panic recovery, goroutine behavior, and context cancellation), plus dedicated tests for commands, the cursed renderer, exec, options, screen, and logging. Concurrency-sensitive test state uses sync/atomic rather than ad hoc locking. Errors are surfaced as explicit sentinel values (ErrProgramPanic, ErrProgramKilled, ErrInterrupted) instead of leaking raw panics to callers. Cross-platform correctness is enforced structurally through Go build-tag file splits (unix vs. windows vs. bsd variants of tty, termios, and signal handling) rather than runtime branching, and a standalone lint CI workflow with a lint-sync companion suggests linting is treated as a first-class, kept-in-sync concern.

API Design The public surface is deliberately minimal: implement three methods on a model type and call tea.NewProgram(model).Run(). Message handling follows Go’s idiomatic type-switch pattern over an interface (Msg), and program configuration uses the functional-options pattern (ProgramOption) so new capabilities (context, custom I/O, environment injection for SSH sessions) can be added without breaking existing call sites. Exported types carry extensive godoc comments with runnable example snippets inline, and a tutorials/ directory plus 30+ programs under examples/ (from a minimal counter to canvas rendering and file pickers) give newcomers a graduated on-ramp. The main real cost for a new user is conceptual, not mechanical: adopting the Elm-style unidirectional message loop is a genuine departure from Go’s typical imperative style, even though the three-method contract itself is small.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers