Terminal UI: BubbleTea (Go) vs Ratatui (Rust)

Published: (February 21, 2026 at 07:21 PM EST)
5 min read
Source: Dev.to

Source: Dev.to

BubbleTea (Go)

What is BubbleTea?
BubbleTea is a Go framework for TUIs based on The Elm Architecture. You describe your app with a model (state) and three pieces:

PiecePurpose
InitReturns an initial command (or nil).
UpdateHandles incoming messages, returns a new model and an optional command.
ViewRenders the UI as a string.

The framework runs the event loop, turns key‑presses and I/O into messages, and redraws the UI whenever the model changes. In short, it’s the fun, stateful way to build terminal apps in Go, with a single source of truth and predictable updates.

Why choose BubbleTea?

  • Production‑ready (v1.x) with tens of thousands of GitHub stars.
  • Works inline, full‑screen, or mixed.
  • Usually paired with Bubbles (components such as inputs, viewports, spinners) and Lip Gloss (styling).
  • Fits naturally into a typical Go project layout (cmd/, packages, etc.).

Minimal BubbleTea program

package main

import (
	"fmt"
	"os"

	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 "ctrl+c", "q":
			return m, tea.Quit
		case "up", "k":
			if m.cursor > 0 {
				m.cursor--
			}
		case "down", "j":
			if m.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"
}

func main() {
	if _, err := tea.NewProgram(model{
		choices:  []string{"Buy carrots", "Buy celery", "Buy kohlrabi"},
		selected: make(map[int]struct{}),
	}).Run(); err != nil {
		fmt.Printf("error: %v", err)
		os.Exit(1)
	}
}

Notable apps built with BubbleTea

  • Crush – Charm’s TUI‑based AI coding agent.
  • Glow – Markdown viewer.
  • Huh – Interactive prompts.
  • Many other tools from the top‑trending Go projects ecosystem.

For larger Go applications you can add dependency injection, solid unit tests, and other Go‑idiomatic patterns; the same ideas apply to BubbleTea models and commands.


Ratatui (Rust)

What is Ratatui?
Ratatui is a Rust library for TUIs that uses immediate‑mode rendering: each frame you describe the entire UI (widgets and layout), and Ratatui draws it.

  • It is lightweight and unopinionated – it does not impose an Elm‑style model or a specific app structure.
  • You keep your own state, run your own event loop (typically with crossterm, termion, or termwiz), and call terminal.draw(|f| { … }) to render.

Elm‑style vs. Immediate‑mode

ApproachWho owns the loop?How UI is described
Elm‑style (BubbleTea)FrameworkUpdateView (only changed parts are redrawn)
Immediate‑mode (Ratatui)You (the app)Every frame you rebuild the whole UI from current state

Why choose Ratatui?

  • Used by 2,100+ crates and trusted by companies such as Netflix (e.g. bpftop), OpenAI, AWS (e.g. amazon-q-developer-cli), and Vercel.
  • Current stable version is 0.30.x with strong documentation and optional back‑ends.
  • Ideal when you need full control over input handling and rendering, or when you’re already working in the Rust ecosystem.

Minimal Ratatui program

use crossterm::event::{self, Event, KeyCode};
use ratatui::{prelude::*, widgets::Paragraph};
use std::time::Duration;

fn main() -> Result<()> {
    // Initialise the terminal
    let mut terminal = ratatui::init();

    loop {
        // Draw the UI each frame
        terminal.draw(|frame| {
            let area = frame.area();
            frame.render_widget(
                Paragraph::new("Hello, Ratatui! Press q to quit.")
                    .alignment(Alignment::Center),
                area,
            );
        })?;

        // Handle input (poll every 250 ms)
        if event::poll(Duration::from_millis(250))? {
            if let Event::Key(key) = event::read()? {
                if key.code == KeyCode::Char('q') {
                    break;
                }
            }
        }
    }

    // Restore the terminal state before exiting
    ratatui::restore();
    Ok(())
}

When to Choose Which?

AspectBubbleTea (Go)Ratatui (Rust)
ArchitectureElm Architecture (model + Init/Update/View)Immediate mode (you own the loop)
EcosystemBubbles, Lip Gloss, 10k+ apps (e.g., Crush)2,100+ crates, used by Netflix, OpenAI, AWS, Vercel
Best forRapid iteration, Go teams, CLIsFull control, performance‑sensitive TUIs, Rust codebases
Typical use‑caseWant the fastest path to a polished TUI in GoNeed maximum control or already in Rust

Bottom line: Both frameworks are excellent. The preferred language and the amount of structure you want from the framework should drive the decision.

Further Reading & Resources

  • Top 19 Trending Go Projects on GitHub – January 2026
  • Top 23 Trending Rust Projects on GitHub – January 2026
  • Dependency Injection in Go: Patterns & Best Practices

Crush UI (screenshot above) is implemented using the BubbleTea framework.

Practices

  • Go SDKs for Ollama – comparison with examples
  • Comparing Go ORMs for PostgreSQL: GORM vs Ent vs Bun vs sqlc
  • Go Project Structure: Practices & Patterns
  • Go Unit Testing: Structure & Best Practices
  • Bubble Tea – The Elm Architecture for Go TUIs
  • Ratatui – Introduction
  • Ratatui – Rendering (immediate mode)
  • Charm Crush – TUI coding agent
  • Ratatui on crates.io
0 views
Back to Blog

Related posts

Read more »

Show HN: I ported Tree-sitter to Go

This started as a hard requirement for my TUI‑based editor application, but it ended up going in a few different directions. - A suite of tools that help with s...