Skip to content
Theme:

Components

The tui crate provides a component-based terminal UI framework. Every interactive element implements the Component trait, which handles events and renders frames.

Cargo.toml
[dependencies]
tui = "0.1"
pub trait Component {
type Message;
fn on_event(&mut self, event: &Event) -> impl Future<Output = Option<Vec<Self::Message>>>;
fn render(&mut self, ctx: &ViewContext) -> Frame;
}
MethodDescription
on_event(event)Handle input, return messages (or None if unhandled)
render(ctx)Render the component into a Frame

Message is an associated type — each component defines its own message enum for communicating state changes upward.

pub enum Event {
Key(KeyEvent),
Paste(String),
Mouse(MouseEvent),
Tick,
Resize(Size),
}
WidgetFeatureDescription
TextFieldSingle-line text input
SelectListScrollable selection list
RadioSelectSingle-choice selection
MultiSelectMulti-choice selection
CheckboxBoolean toggle
NumberFieldNumeric input
FormMulti-field form container
SpinnerLoading indicator
PanelBordered container
BorderedTextFieldSingle-line input with labeled border
StepperHorizontal multi-step progress indicator
SplitPanelTwo-panel layout with vertical divider
GalleryBrowseable component list with preview pane
ComboboxpickerFuzzy-searchable dropdown
use tui::text_field::TextField;
let mut field = TextField::new("Enter your name:");
// Handle events and render in your component loop
use tui::select_list::{SelectList, SelectItem};
let items = vec![
SelectItem::new("opt1", "Option One"),
SelectItem::new("opt2", "Option Two"),
];
let mut list = SelectList::new(items);

A single-line text input rendered inside a box with the label intersecting the top border:

use tui::components::bordered_text_field::BorderedTextField;
let mut field = BorderedTextField::new("Name", "my-agent".into());
field.set_width(40);

A horizontal multi-step progress indicator showing complete, current, and upcoming steps. Renders a single Line, not a full Component:

use tui::components::stepper::{Stepper, StepperItem, StepVisualState};
let items = vec![
StepperItem { label: "Configure", state: StepVisualState::Complete },
StepperItem { label: "Install", state: StepVisualState::Current },
StepperItem { label: "Finish", state: StepVisualState::Upcoming },
];
let stepper = Stepper { items: &items, separator: " > ", leading_padding: 2 };
let line = stepper.render(&ctx);

A two-panel layout with a vertical divider:

use tui::components::split_panel::{SplitPanel, SplitLayout};
let layout = SplitLayout::fraction(1, 3, 20, 60); // 1/3 of width, clamped [20, 60]
let split = SplitPanel::new(left_component, right_component, layout);

A browseable list of named components with a live preview pane (built on SplitPanel):

use tui::components::gallery::Gallery;
let entries = vec![
("TextField".into(), text_field_demo),
("Checkbox".into(), checkbox_demo),
];
let gallery = Gallery::new(entries);

A searchable dropdown with fuzzy matching:

use tui::{Combobox, Searchable};
#[derive(Clone)]
struct MyItem { name: String }
impl Searchable for MyItem {
fn search_text(&self) -> &str { &self.name }
}
let items = vec![MyItem { name: "foo".into() }];
let mut picker = Combobox::new(items);

Manages focus across multiple components:

use tui::FocusRing;
let mut focus = FocusRing::new(3); // 3 focusable elements
focus.next(); // Move focus forward
focus.prev(); // Move focus backward
let current = focus.current(); // Current focused index

Components compose by embedding child components and delegating events:

struct MyApp {
input: TextField,
list: SelectList<String>,
}
impl Component for MyApp {
type Message = AppMessage;
async fn on_event(&mut self, event: &Event) -> Option<Vec<AppMessage>> {
// Delegate to focused child
if let Some(msgs) = self.input.on_event(event).await {
// Handle TextField messages
}
None
}
fn render(&mut self, ctx: &ViewContext) -> Frame {
let input_frame = self.input.render(ctx);
let list_frame = self.list.render(ctx);
// Combine frames vertically
Frame::new(
input_frame.into_lines().into_iter()
.chain(list_frame.into_lines())
.collect()
)
}
}