NAME
tamboui — A Java library for building modern terminal user interfaces.
SYNOPSIS
https://github.com/tamboui/tamboui/releasesINFO
DESCRIPTION
A Java library for building modern terminal user interfaces.
README
TamboUI
A Java library for building modern terminal user interfaces.
TamboUI brings the TUI paradigms seen in things like Rust's ratatui or Go's bubbletea to the Java ecosystem.
TamboUI is pronounced like "tambouille" (\tɑ̃.buj) in French, or "tan-bouy".
It provides a comprehensive set of widgets and a layout system for building rich terminal applications with modern Java idioms.
Note: TamboUI is still experimental and under active development. APIs may/will change. Feedback, issues, and contributions are welcome!
Snapshots
Currently TamboUI is only available as snapshot builds. You can use the following Maven repository to access snapshot versions:
🚀 Maven
<repository>
<id>ossrh-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
🛠️ Gradle
repositories {
maven {
url = uri("https://central.sonatype.com/repository/maven-snapshots/")
mavenContent {
snapshotsOnly()
}
}
}
📦 JBang
//REPOS https://central.sonatype.com/repository/maven-snapshots/
NOTE: If you are using JBang 0.136 or higher you can use the shortcut // REPOS central-portal-snapshots
Key Features
- Immediate-mode rendering - Redraw the entire UI each frame for simple state management
- Intermediate buffer system - Widgets render to a buffer, enabling diff-based terminal updates
- Constraint-based layout - Flexible layout system with percentage, fixed, ratio, and proportional sizing
- Multiple backends - Cross-platform terminal support using JLine 3, Panama, Aesh, and more
- High-level TUI framework - TuiRunner eliminates boilerplate with built-in event handling and Cascading Style Sheets (CSS) support
- PicoCLI integration - Optional module for CLI argument parsing
- GraalVM native image support - Compile to native executables for instant startup
- Works everywhere - Core is Java 8+ compatible, but following patterns that works really well with modern Java idioms
Modules
| Module | Description |
|---|---|
tamboui-core | Core types: Buffer, Cell, Rect, Style, Layout, Text primitives |
tamboui-widgets | All widget implementations (see below) |
tamboui-jline | JLine 3 terminal backend |
tamboui-tui | High-level TUI framework with TuiRunner, event handling, and key helpers |
tamboui-toolkit | Fluent DSL for declarative UI construction with components and focus management |
tamboui-picocli | Optional PicoCLI integration for CLI argument parsing |
tamboui-core-assertj | AssertJ assertions for things like Buffer comparison |
demos/* | Demo applications showcasing widgets and features |
Requirements
- Java 8 or later, Java 17+ highly recommended
- Gradle 9.x
JFR tracing
TamboUI can emit JFR events for diagnostics and performance analysis when a recording is active.
jcmd <pid> JFR.start name=tamboui settings=profile duration=60s filename=tamboui.jfr
Event names:
dev.tamboui.terminal.draw(duration ofTerminal.draw())dev.tamboui.toolkit.route(duration of toolkit event routing)dev.tamboui.toolkit.candidatedev.tamboui.toolkit.focus.changedev.tamboui.toolkit.focus.navigationdev.tamboui.toolkit.drag.statedev.tamboui.toolkit.global.handler
Quick Start
Using TuiRunner (Recommended)
The tamboui-tui module provides a high-level framework that eliminates boilerplate:
//DEPS dev.tamboui:tamboui-tui:LATEST //DEPS dev.tamboui:tamboui-jline3-backend:LATESTpackage dev.tamboui.demo;
import dev.tamboui.tui.TuiRunner; import dev.tamboui.tui.event.KeyEvent; import dev.tamboui.widgets.paragraph.Paragraph; import dev.tamboui.text.Text;
public class HelloWorldTuiDemo { public static void main(String[] args) throws Exception { try (var tui = TuiRunner.create()) { tui.run( (event, runner) -> switch (event) { case KeyEvent k when k.isQuit() -> { runner.quit(); yield false; } default -> false; }, frame -> { var paragraph = Paragraph.builder() .text(Text.from("Hello, TamboUI! Press 'q' to quit.")) .build(); frame.renderWidget(paragraph, frame.area()); } ); } } }
Save the above file to HelloWorld.java and you can run it as jbang HelloWorld.java
With Mouse and Animation
import dev.tamboui.tui.TuiConfig; import dev.tamboui.tui.TuiRunner; import java.time.Duration;var config = TuiConfig.builder() .mouseCapture(true) .tickRate(Duration.ofMillis(16)) // ~60fps animation .build();
try (var tui = TuiRunner.create(config)) { tui.run( (event, runner) -> switch (event) { case KeyEvent k when Keys.isQuit(k) -> { runner.quit(); yield false; } case MouseEvent m -> { handleMouse(m); yield true; } case TickEvent t -> { updateAnimation(t); yield true; } default -> false; }, frame -> render(frame) ); }
With PicoCLI Integration
//DEPS dev.tamboui:tamboui-picocli:LATEST //DEPS dev.tamboui:tamboui-jline3-backend:LATESTimport dev.tamboui.picocli.TuiCommand; import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.Option;
@Command(name = "myapp", mixinStandardHelpOptions = true) public class MyApp extends TuiCommand {
@Option(names = {"-t", "--title"}, description = "Window title") private final String title = "My App"; @Override protected void runTui(TuiRunner runner) throws Exception { runner.run(this::handleEvent, this::render); } public static void main(String[] args) { System.exit(new CommandLine(new MyApp()).execute(args)); }
}
Using the declarative Toolkit
The tamboui-toolkit module provides a fluent DSL giving you retained mode declarative UI construction:
//DEPS dev.tamboui:tamboui-toolkit:LATEST //DEPS dev.tamboui:tamboui-jline3-backend:LATESTimport static dev.tamboui.toolkit.Toolkit.*; import dev.tamboui.toolkit.app.ToolkitApp; import dev.tamboui.toolkit.element.Element;
public class HelloDsl extends ToolkitApp {
@Override protected Element render() { return panel("Hello", text("Welcome to TamboUI DSL!").bold().cyan(), spacer(), text("Press 'q' to quit").dim() ).rounded(); } public static void main(String[] args) throws Exception { new HelloDsl().run(); }
}
Toolkit Elements
| Element | Factory Method | Description |
|---|---|---|
| Text | text("Hello") | Styled text content |
| Panel | panel("Title", ...) | Bordered container with title |
| Row | row(a, b, c) | Horizontal layout |
| Column | column(a, b, c) | Vertical layout |
| Spacer | spacer() | Flexible empty space |
| Gauge | gauge(0.75) | Progress bar |
| LineGauge | lineGauge(50) | Single-line progress indicator |
| Sparkline | sparkline(1,2,3,4,5) | Mini data chart |
| List | list("A", "B", "C") | Selectable list |
| Table | table() | Data table with rows/columns |
| Tabs | tabs("Home", "Settings") | Tab bar |
| TextInput | textInput(state) | Text input field |
| BarChart | barChart(10, 20, 30) | Bar chart |
| Chart | chart() | Line/scatter plots |
| Canvas | canvas() | Drawing surface |
| Calendar | calendar() | Monthly calendar |
| Scrollbar | scrollbar() | Scroll position indicator |
Toolkit Examples
// Progress with color coding gauge(0.75) .label("Loading...") .gaugeColor(Color.GREEN) .title("Progress") .rounded()// Sparkline chart sparkline(cpuHistory) .color(Color.CYAN) .title("CPU Usage") .rounded()
// Selectable list list("Option 1", "Option 2", "Option 3") .state(listState) .highlightColor(Color.YELLOW) .title("Menu") .rounded()
// Tab bar tabs("Home", "Settings", "About") .selected(0) .highlightColor(Color.CYAN)
// Table with data table() .header("Name", "Age", "City") .row("Alice", "30", "NYC") .row("Bob", "25", "LA") .widths(Constraint.percentage(40), Constraint.length(10), Constraint.fill()) .title("Users") .rounded()
// Canvas drawing canvas(-10, 10, -10, 10) .paint(ctx -> { ctx.draw(new Circle(0, 0, 5, Color.RED)); ctx.draw(new Line(-5, -5, 5, 5, Color.GREEN)); }) .title("Drawing") .rounded()
Features
- Static imports -
text(),panel(),row(),column(),spacer(), and all widget factories - Fluent styling -
.bold(),.cyan(),.onBlue(),.rounded(),.borderColor() - Layout constraints -
.length(n),.percent(n),.fill(),.min(n),.max(n) - Automatic focus - Tab navigation, click-to-focus
- Component events - Components handle their own key/mouse events via
.onKeyEvent()and.onMouseEvent() - Drag support -
.draggable()for movable elements
Immediate Mode API
For more control, use the terminal directly:
//DEPS dev.tamboui:tamboui-widgets:LATEST //DEPS dev.tamboui:tamboui-jline3-backend:LATESTimport dev.tamboui.terminal.Backend; import dev.tamboui.terminal.BackendFactory; import dev.tamboui.terminal.Terminal;
try (var backend = BackendFactory.create()) { backend.enableRawMode(); backend.enterAlternateScreen();
var terminal = new Terminal<>(backend); terminal.draw(frame -> { // render widgets... });
}
Building
Building the Project
./gradlew assemble
Running Tests
./gradlew test
Publishing to Local Maven Repository
Useful when using when you want to use the library in your own Maven or JBang project.
./gradlew publishToMavenLocal
Publishing to the build directory
This method is preferred to publishing to Maven local to see what will actually be published:
./gradlew publishAllPublicationsToBuildRepository
Then look into build/repo for the generated artifacts.
Running Demos
To run the demos, you can either run them on the JVM or compile them to a native executable.
Using jbang
JBang
If you have JBang installed you can list the local catalog:
jbang alias list .
And then pick one you want to run, i.e.:
jbang sparkline-demo
If your IDE supports JBang you can open the demo .java files and run them directly from your IDE too.
Bash
You can use the ./run-demo.sh script to run any demo directly without building/installing first. For example, to run the sparkline-demo:
./run-demo.sh sparkline-demo
To run it as a native executable:
./run-demo.sh sparkline-demo --native
JVM
To run a demo on the JVM, first install the distribution and then execute the generated script.
For example, to run the sparkline-demo:
./gradlew :demos:sparkline-demo:installDist
./demos/sparkline-demo/build/install/sparkline-demo/bin/sparkline-demo
Native Image (requires GraalVM)
To run as a native executable:
./gradlew :demos:sparkline-demo:nativeCompile
./demos/sparkline-demo/build/native/nativeCompile/sparkline-demo
Replace sparkline-demo with the name of any other demo (e.g., basic-demo, tui-demo, toolkit-demo).
Widgets
All standard ratatui widgets are implemented:
| Widget | Type | Description |
|---|---|---|
| Block | Stateless | Container with borders, titles, and padding |
| Paragraph | Stateless | Multi-line text with wrapping and alignment |
| List | Stateful | Scrollable list with selection |
| Table | Stateful | Grid with rows, columns, and selection |
| Tabs | Stateful | Tab bar with selection |
| Gauge | Stateless | Progress bar with percentage |
| LineGauge | Stateless | Horizontal line progress indicator |
| Sparkline | Stateless | Mini line chart for data series |
| BarChart | Stateless | Vertical bar chart |
| Chart | Stateless | Line and scatter plots with axes |
| Canvas | Stateless | Drawing surface with shapes (braille/block characters) |
| Calendar | Stateless | Monthly calendar with date styling |
| Scrollbar | Stateful | Visual scroll position indicator |
| Clear | Stateless | Clears an area (for widget layering) |
| TextInput | Stateful | Single-line text input (TamboUI addition) |
Demo Applications
| Demo | Description |
|---|---|
hello-world-demo | Minimal Hello World using TuiRunner |
basic-demo | Interactive list with text input |
tui-demo | Showcases TuiRunner with keyboard, mouse, and animation |
toolkit-demo | Widget Playground showcasing DSL with draggable panels |
jtop-demo | System monitor (like "top") using the DSL |
picocli-demo | PicoCLI integration with CLI options |
gauge-demo | Progress bars and line gauges |
table-demo | Table widget with selection |
tabs-demo | Tab navigation |
scrollbar-demo | Scrollbar with content |
sparkline-demo | Sparkline data visualization |
barchart-demo | Bar chart widget |
chart-demo | Line and scatter charts |
canvas-demo | Canvas with shapes and braille drawing |
calendar-demo | Monthly calendar with events |
Key Bindings (via Keys utility)
The Keys class provides helpers for common key patterns:
import static dev.tamboui.tui.Keys.*;// Quit patterns isQuit(event) // q, Q, or Ctrl+C
// Vim-style navigation (also accepts arrow keys) isUp(event) // Up arrow or k/K isDown(event) // Down arrow or j/J isLeft(event) // Left arrow or h/H isRight(event) // Right arrow or l/L
// Page navigation isPageUp(event) // PageUp or Ctrl+U isPageDown(event) // PageDown or Ctrl+D isHome(event) // Home or g isEnd(event) // End or G
// Actions isSelect(event) // Enter or Space isEscape(event) // Escape isTab(event) // Tab isBackTab(event) // Shift+Tab
Contributors
Cédric Champeau 🚇 ⚠️ 💻 | Max Rydahl Andersen 🚇 ⚠️ 💻 | Claus Ibsen 💻 | Andres Almiray 🚇 | Łukasz Kapica 📖 🚇 |
Acknowledgments
This project started as a port of ratatui, an excellent Rust TUI library. We thank the ratatui maintainers and contributors for their work.
License
MIT License - see LICENSE for details.