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!
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
- 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
| 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 |
- Java 8 or later, Java 17+ highly recommended
- Gradle 9.x
The tamboui-tui module provides a high-level framework that eliminates boilerplate:
//DEPS dev.tamboui:tamboui-tui:LATEST
//DEPS dev.tamboui:tamboui-jline3-backend:LATEST
package 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
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)
);
}//DEPS dev.tamboui:tamboui-picocli:LATEST
//DEPS dev.tamboui:tamboui-jline3-backend:LATEST
import 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));
}
}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:LATEST
import 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();
}
}| 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 |
// 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()- 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
For more control, use the terminal directly:
//DEPS dev.tamboui:tamboui-widgets:LATEST
//DEPS dev.tamboui:tamboui-jline3-backend:LATEST
import 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...
});
}./gradlew assemble./gradlew testUseful when using when you want to use the library in your own Maven or JBang project.
./gradlew publishToMavenLocalThis method is preferred to publishing to Maven local to see what will actually be published:
./gradlew publishAllPublicationsToBuildRepositoryThen look into build/repo for the generated artifacts.
To run the demos, you can either run them on the JVM or compile them to a native executable.
Using 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.
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-demoTo run it as a native executable:
./run-demo.sh sparkline-demo --nativeTo 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-demoTo run as a native executable:
./gradlew :demos:sparkline-demo:nativeCompile
./demos/sparkline-demo/build/native/nativeCompile/sparkline-demoReplace sparkline-demo with the name of any other demo (e.g., basic-demo, tui-demo, toolkit-demo).
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 | 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 |
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+TabCédric Champeau 🚇 |
Max Rydahl Andersen 🚇 |
Claus Ibsen 💻 |
Andres Almiray 🚇 |
Łukasz Kapica 📖 🚇 |
This project started as a port of ratatui, an excellent Rust TUI library. We thank the ratatui maintainers and contributors for their work.
MIT License - see LICENSE for details.