Bubbletea
An Elm-architecture TUI framework for Rust: pure update/view, command-driven
effects, and a deterministic event loop.
TL;DR
The Problem: Terminal apps often mix state mutation and I/O in ways that are hard to test and reason about.
The Solution: Bubbletea separates pure state transitions from effects.
Model + Message + Cmd gives you a testable core and a predictable runtime.
Why Bubbletea
- Predictable: pure
updateandviewfunctions. - Composable: child models compose cleanly.
- Testable: drive updates with messages and assert on views.
- Async-ready: optional async runtime support.
Role in the charmed_rust (FrankenTUI) stack
Bubbletea is the runtime core. bubbles, huh, glow, and wish all build on
bubbletea’s Model abstraction. lipgloss styles the output, and harmonica
provides time-based motion helpers used in animations.
Crates.io package
Package name: charmed-bubbletea
Library crate name: bubbletea
Installation
[]
= { = "charmed-bubbletea", = "0.1.2" }
Enable optional features:
= { = "charmed-bubbletea", = "0.1.2", = ["async", "macros"] }
Quick Start
use ;
Core Concepts
- Model: your application state and logic.
- Message: events that drive state transitions.
- Cmd: effect descriptions (I/O, timers, async work).
- Program: event loop runner.
API Overview
Key types live in crates/bubbletea/src/:
model.rs:Modeltrait and lifecycle hooks.message.rs:Messagetype and downcasting helpers.cmd.rs: command helpers (Cmd::none,Cmd::batch,Cmd::quit).program.rs: event loop and runner configuration.key.rs: key event types and helpers.
Feature Flags
macros: derive macros forModelviabubbletea-macros.async: async runtime integration (tokio-based).thread-pool: batch commands with rayon.
Testing Guidance
Test update and view directly:
let mut app = Counter ;
app.update;
assert_eq!;
For headless runs:
let model = new.without_renderer.run.unwrap;
Troubleshooting
- No input events: ensure the program is running in a tty.
- Output garbled: disable alternate screen or set a compatible
TERM. - Async commands not running: enable the
asyncfeature and usetokio.
Limitations
- Terminal rendering is bound by terminal capabilities.
- High-frequency animation depends on your event loop tick rate.
FAQ
Can I use bubbletea without lipgloss?
Yes. Bubbletea renders strings; you can render them any way you want.
Do I need the macros crate?
No. It’s optional and only adds ergonomics.
Is async required?
No. Synchronous apps work out of the box.
About Contributions
Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via gh and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
License
MIT. See LICENSE at the repository root.