# charmbracelet/bubbles: TUI components for Bubble Tea, from spinner to file picker

> Bubbles is a Go library of terminal UI primitives that plug into Bubble Tea's Elm-style update loop. It suits Go developers building interactive CLIs, and it assumes you have already chosen Bubble Tea as your runtime.

**charmbracelet/bubbles** — TUI components for Bubble Tea 🫧

- Repository: https://github.com/charmbracelet/bubbles
- Stars: 8,951 · Forks: 468
- Language: Go
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/charmbracelet-bubbles

## The gap Bubbles fills between Bubble Tea and a finished CLI

Bubble Tea gives you an application loop: a model, an Update function that consumes messages, and a View function that returns a string. It does not give you a cursor that blinks in a text field, a table that scrolls, or a fuzzy filter over a list of items. That is the layer Bubbles occupies. The README calls the components "primitives for Bubble Tea applications" and notes they are used in production in Crush, Charm's own terminal client.

The intended reader is a Go developer who has already decided on Bubble Tea and now needs widgets. The repository layout makes the scope concrete: cursor, filepicker, help, key, list, paginator, progress, spinner, stopwatch, table, textarea, textinput, timer, tree and viewport are each a directory with its own package. There is no renderer, no event loop and no styling engine in Bubbles itself; lipgloss and bubbletea are dependencies in go.mod, not things Bubbles replaces.

That division matters when you evaluate it. If you want a framework that owns the whole screen, Bubbles is not that. If you want a set of pieces you assemble inside a loop you already control, that is exactly the shape it has.

## How a Bubbles component fits into the Bubble Tea update loop

Every component follows the same contract. You embed it in your model, the component exposes an Init, an Update that takes a tea.Msg and returns the component plus a tea.Cmd, and a View that returns a string. Your own Update forwards messages to the child and stores the returned value back into your model. There is no registry, no global state and no lifecycle hook beyond that.

The key package is the clearest example of the pattern because it is non-visual. It manages key bindings and matching, and the help package consumes those bindings to generate a help view automatically. That coupling is the design: bindings declared once are reused for input handling and for the help text, so the two cannot drift apart.

State lives in your struct, not inside a singleton, which is why two Bubbles instances in the same program do not interfere. The cost is that you write the forwarding code yourself for every component you add, and a component that needs to be resized requires you to call its SetWidth or SetHeight equivalent when the terminal size message arrives. The README does not describe a helper that propagates window size to child components automatically.

## Installing Bubbles and wiring a text input into a first program

The module path in go.mod is charm.land/bubbles/v2, so that is the import path to use. The README links example code for a single text input field at examples/textinput/main.go in the Bubble Tea repository, and for many fields at examples/textinputs/main.go. Those linked examples are the reference to follow for the exact construction and focus calls; the README itself gives no inline snippet, only the links.

The same pattern repeats for every component: the README links an example per component rather than printing code. For the spinner it points at examples/spinner/main.go and examples/spinners/main.go, for the table at examples/table/main.go, for the viewport at examples/pager/main.go, and for the file picker at examples/file-picker/main.go, all under the Bubble Tea repository.

Swapping in a different component follows the same three steps: construct it, forward messages to it, render its View. The list component is the largest of these and, per the README, bundles pagination, fuzzy filtering, an auto-generated help view, an activity spinner and status messages, each of which can be enabled or disabled. The README links a default list example, a simple list example and an all-features example for it.

## Where Bubbles stops being the right choice

The list component is the clearest case of a component that is opinionated by design. The README says it was extrapolated from Glow, and it ships with filtering, pagination, help and a spinner already wired together. If your list needs a layout or an interaction model that does not resemble that bundle, you will spend more time disabling parts than you would writing the loop yourself against the key and viewport packages.

The second constraint is the dependency graph. go.mod requires charm.land/bubbletea/v2 v2.0.9 and charm.land/lipgloss/v2 v2.0.6, along with the x/ansi, uniseg, go-runewidth and fuzzy packages. If your project is pinned to Bubble Tea v1, Bubbles v2 is not a drop-in; the README points v1 users at UPGRADE_GUIDE_V2.md, which is the document that describes the migration. That guide is also the honest signal that this is a major-version boundary rather than a patch.

The third is platform behaviour that the README does not cover. Pasting in the text input and text area depends on the atotto/clipboard dependency, and the README does not document what happens when no clipboard mechanism is available in the environment. Mouse wheel support is mentioned for the viewport, and the README does not state which terminals it was verified against. If your target is a restricted environment such as a CI runner or a container without a terminal, verify those paths yourself before you build a workflow around them.

## Bubbles compared with building raw ANSI output or using a full TUI framework

The alternative on one side is writing escape sequences yourself, or using a lower-level library such as tcell, which gives you a cell grid and input events but no model-update-view structure and no ready-made widgets. With that approach you own the diffing, the cursor placement and the redraw logic. Bubbles hands those to Bubble Tea and gives you components whose View methods return strings.

The alternative on the other side is a framework that ships complete screens, such as a form or a dashboard, rather than parts. Bubbles deliberately sits below that line: the README describes the list as customizable and the table as supporting vertical scrolling, but neither is a complete application screen. You still write the model.

A third comparison worth naming is the viewport plus Reflow combination. The README states the viewport is well complemented by muesli/reflow for ANSI-aware indenting and text wrapping, which means the wrapping behaviour is not inside the viewport package. If your content contains ANSI styling and you do not add a wrapping step, the viewport will scroll content that is already too wide. That is not a defect so much as an unstated assembly step, and it is the kind of thing the README leaves to the linked packages.

## Licence terms and what an upgrade costs

The repository carries an MIT licence, and the LICENSE file sits at the top level alongside README.md, go.mod and UPGRADE_GUIDE_V2.md. MIT is permissive and imposes no copyleft obligation on your own code, but nothing here is legal advice and the LICENSE file is the text that governs.

The upgrade picture is the part to plan for. The module path is charm.land/bubbles/v2, the current releases are v2.2.1, v2.2.0 and v2.1.1, and the README opens with a tip aimed at people coming from v1: read the upgrade guide, or, in the README's phrasing, point an LLM at it. That framing tells you the maintainers expect the v1 to v2 move to involve real code changes rather than a version bump in go.mod.

On cadence, the last push to the repository was on 2026-09-06, and the most recent tagged release is v2.2.1 from 2026-08-24. Those are the only maintenance signals available in the repository metadata. There is no published support window or deprecation policy in the README, so pinning a version in go.mod and reading UPGRADE_GUIDE_V2.md before a major jump is the practical way to control the cost.

## Conclusion

Adopt charmbracelet/bubbles if your CLI is already a Bubble Tea program and you need a text input, list, viewport or file picker rather than hand-rolling cursor math. Do not adopt it for a non-Go project, or for a TUI framework that is not Bubble Tea, because every component's Update and View methods are written against Bubble Tea's message and command types. Before committing, read UPGRADE_GUIDE_V2.md, since the module path is charm.land/bubbles/v2 and the README points v1 users at that guide. Then check the Go version in your own go.mod against the go 1.25.0 directive in the library's go.mod.

## FAQ

### What is charmbracelet/bubbles?

It is a Go library of terminal UI primitives for Bubble Tea applications, covering components such as spinner, text input, text area, table, progress, paginator, viewport, list, file picker, timer, stopwatch, help and key. The README describes them as primitives for Bubble Tea applications and notes they are used in production in Crush.

### How do I install charmbracelet/bubbles?

The module path in go.mod is charm.land/bubbles/v2, so that is the import path to use in your own module. That pulls in charm.land/bubbletea/v2 and charm.land/lipgloss/v2 as direct dependencies.

### Does charmbracelet/bubbles work without Bubble Tea?

No. The components are built against Bubble Tea's message and command types, and go.mod requires charm.land/bubbletea/v2. The README presents the library as primitives for Bubble Tea applications, not as a standalone renderer.

### What is the licence for charmbracelet/bubbles?

The repository is MIT licensed, with the LICENSE file at the top level. MIT is permissive and does not impose copyleft terms on your own code, but the LICENSE text is what governs.

## Sources

- [charmbracelet/bubbles on GitHub](https://github.com/charmbracelet/bubbles)
- [Issues](https://github.com/charmbracelet/bubbles/issues)
- [License: MIT](https://github.com/charmbracelet/bubbles/blob/main/LICENSE)
- [README](https://github.com/charmbracelet/bubbles/blob/main/README.md)
- [Releases](https://github.com/charmbracelet/bubbles/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/charmbracelet-bubbles
