ConfigsApr 12, 2026·2 min read

Bubble Tea — Powerful TUI Framework for Go

Bubble Tea is a powerful little TUI framework for Go based on The Elm Architecture. Build terminal applications with composable components, events, and commands. Part of the Charm ecosystem (Lip Gloss, Gum, Wish, Huh). Powers lazygit, soft-serve, and many CLI tools.

TL;DR
Bubble Tea is a Go TUI framework based on the Elm Architecture for building composable, event-driven terminal applications.
§01

What it is

Bubble Tea is a terminal user interface (TUI) framework for Go, built on the Elm Architecture pattern. You define a model (state), an update function (handles messages), and a view function (renders the UI). The framework handles the event loop, terminal rendering, and input management.

Bubble Tea is part of the Charm ecosystem, which includes Lip Gloss (styling), Bubbles (pre-built components), Gum (shell scripting), and Wish (SSH apps). It powers tools like lazygit, soft-serve, and many CLI applications.

§02

How it saves time or tokens

Bubble Tea provides a structured architecture for TUI development. Without it, building interactive terminal apps involves raw ANSI escape codes, manual input handling, and custom rendering loops. Bubble Tea abstracts these concerns into a clean Model-Update-View cycle.

The Bubbles component library provides pre-built widgets (text inputs, spinners, lists, tables, file pickers) that you compose into complex UIs without writing rendering logic from scratch.

§03

How to use

  1. Add Bubble Tea to your Go project:
go get github.com/charmbracelet/bubbletea
  1. Define a model, update, and view:
package main

import (
    "fmt"
    tea "github.com/charmbracelet/bubbletea"
)

type model struct {
    choices  []string
    cursor   int
    selected map[int]struct{}
}

func (m model) Init() tea.Cmd { return nil }

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "q":
            return m, tea.Quit
        case "up":
            if m.cursor > 0 { m.cursor-- }
        case "down":
            if m.cursor < len(m.choices)-1 { m.cursor++ }
        case "enter":
            if _, ok := m.selected[m.cursor]; ok {
                delete(m.selected, m.cursor)
            } else {
                m.selected[m.cursor] = struct{}{}
            }
        }
    }
    return m, nil
}

func (m model) View() string {
    s := "Pick items:\n\n"
    for i, choice := range m.choices {
        cursor := " "
        if m.cursor == i { cursor = ">" }
        checked := " "
        if _, ok := m.selected[i]; ok { checked = "x" }
        s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
    }
    return s + "\nPress q to quit.\n"
}
  1. Run the program:
func main() {
    p := tea.NewProgram(model{
        choices: []string{"Option A", "Option B", "Option C"},
        selected: make(map[int]struct{}),
    })
    p.Run()
}
§04

Example

Using Lip Gloss for styled output:

import "github.com/charmbracelet/lipgloss"

var style = lipgloss.NewStyle().
    Bold(true).
    Foreground(lipgloss.Color("205")).
    Border(lipgloss.RoundedBorder())

func (m model) View() string {
    return style.Render("Styled TUI content")
}
§05

Related on TokRepo

§06

Common pitfalls

  • Mutating the model directly instead of returning a new model from Update. The Elm Architecture expects immutable updates. Always return a new or modified model from the Update function.
  • Blocking in the Update function. Long-running operations should be dispatched as Cmds (async functions). Blocking the Update function freezes the UI.
  • Not using the Bubbles component library. Before building custom widgets, check if Bubbles already has what you need (text input, spinner, progress bar, viewport, table).

Frequently Asked Questions

What is the Elm Architecture and why does Bubble Tea use it?+

The Elm Architecture is a pattern from the Elm programming language: Model (state) + Update (state transitions) + View (rendering). It makes state management predictable and testable. Bubble Tea adopts this pattern for terminal UIs because it cleanly separates logic from presentation.

Can Bubble Tea render colors and styles?+

Yes, via Lip Gloss, the styling library in the Charm ecosystem. Lip Gloss provides CSS-like styling for terminal output: colors, borders, padding, alignment. It handles terminal color capability detection automatically.

What are Bubbles components?+

Bubbles is a companion library of pre-built Bubble Tea components: text inputs, spinners, lists, tables, file pickers, progress bars, and paginated views. Each component implements the Bubble Tea Model interface and can be composed into larger UIs.

Can I build SSH-accessible TUI apps with Bubble Tea?+

Yes. Wish is a Charm library that serves Bubble Tea apps over SSH. Users connect via 'ssh myapp.example.com' and interact with the TUI in their terminal. This is how soft-serve (Git server) works.

How does Bubble Tea compare to tview for Go TUIs?+

tview provides a higher-level widget-based API (forms, tables, pages). Bubble Tea provides a lower-level architecture (Elm pattern) with more control. Bubble Tea is more flexible for custom UIs; tview is faster for standard form-based interfaces.

Citations (3)

Discussion

Sign in to join the discussion.
No comments yet. Be the first to share your thoughts.

Related Assets