How to Use bubbletea for Terminal UIs in Go

Web
Bubbletea is a Go framework that builds terminal user interfaces using the Model-View-Update (MVU) pattern, where you define a model, handle events in an update function, and render the view.

Bubbletea is a Go framework that builds terminal user interfaces using the Model-View-Update (MVU) pattern, where you define a model, handle events in an update function, and render the view. You initialize a program with a model, start the tea loop, and let the framework handle input, timers, and rendering automatically.

Here is a minimal example of a counter application:

package main

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

type model struct {
	count int
}

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 "ctrl+c", "q":
			return m, tea.Quit
		case "up", " ":
			m.count++
		case "down":
			m.count--
		}
	}
	return m, nil
}

func (m model) View() string {
	return fmt.Sprintf("Count: %d\n(q to quit, up/down to change)", m.count)
}

func main() {
	p := tea.NewProgram(model{})
	if _, err := p.Run(); err != nil {
		fmt.Printf("Error: %v", err)
	}
}

For more complex UIs, you often need to handle asynchronous updates or external events. You can do this by returning commands from your Update function. Here is how you might implement a simple timer that updates every second:

package main

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

type tickMsg time.Time

type model struct {
	startedAt time.Time
}

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

func tickCmd() tea.Cmd {
	return tea.Tick(time.Second, func(t time.Time) tea.Msg {
		return tickMsg(t)
	})
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		if msg.String() == "q" || msg.String() == "ctrl+c" {
			return m, tea.Quit
		}
	case tickMsg:
		// Update state based on tick
	}
	return m, tickCmd()
}

func (m model) View() string {
	elapsed := time.Since(m.startedAt)
	return fmt.Sprintf("Running for: %v\n(q to quit)", elapsed)
}

func main() {
	p := tea.NewProgram(model{startedAt: time.Now()})
	if _, err := p.Run(); err != nil {
		fmt.Printf("Error: %v", err)
	}
}

Install the library with go get github.com/charmbracelet/bubbletea. The framework handles the terminal buffer, so you don't need to manage escape codes manually. Just focus on your state transitions and return the string you want displayed in View().