Crate tuirealm[][src]

Expand description


tui-realm is a tui framework to build applications with a React/Elm inspired-by approach

Get Started

Adding tui-realm as dependency

tuirealm = "0.4.3"

or if you want the std components library

tuirealm = { version = "0.3.2", features = [ "with-components" ] }

Since the library requires crossterm as backend, you will be required also to put crossterm as dependency:

crossterm = { version = "0.19" }

Let’s implement a View with update and view functions

This is the most important part, since we’re going to implmenent the main function with all the logic. Let’s underline our purpose:

  1. we want to declare a view, which will be a container and a façade for our components and will handle focus for us
  2. we want to mount some components inside the view
  3. we want to periodically refresh the view, through the view() function, which will use the render() method of each component
  4. we want to handle events through messages Msg using an update() function.
extern crate crossterm;
extern crate tuirealm;
extern crate tui;

use crossterm::event::{poll, read, Event};
use crossterm::event::DisableMouseCapture;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crossterm::execute;
use crossterm::terminal::{
    disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,

use std::io::{stdout, Stdout};
use std::thread::sleep;
use std::time::{Duration, Instant};

use tuirealm::components::input;
use tuirealm::props::borders::{BorderType, Borders};
use tuirealm::{InputType, Msg, Payload, PropsBuilder, Update, Value, View};

// tui
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::Color;
use tui::Terminal;

pub const MSG_KEY_ESC: Msg = Msg::OnKey(KeyEvent {
    code: KeyCode::Esc,
    modifiers: KeyModifiers::NONE,


// Let's setup an input handler

pub(crate) struct InputHandler;

impl InputHandler {

    pub fn new() -> InputHandler {
        InputHandler {}

    pub fn read_event(&self) -> Result<Option<Event>, ()> {
        if let Ok(available) = poll(Duration::from_millis(10)) {
            match available {
                true => {
                    // Read event
                    if let Ok(ev) = read() {
                    } else {
                false => Ok(None),
        } else {

// Let's setup a Context

pub struct Context {
    pub(crate) input_hnd: InputHandler,
    pub(crate) terminal: Terminal<CrosstermBackend<Stdout>>,

impl Context {
    pub fn new() -> Context {
        let _ = enable_raw_mode();
        // Create terminal
        let mut stdout = stdout();
        assert!(execute!(stdout, EnterAlternateScreen).is_ok());
        Context {
            input_hnd: InputHandler::new(),
            terminal: Terminal::new(CrosstermBackend::new(stdout)).unwrap(),

    pub fn enter_alternate_screen(&mut self) {
        let _ = execute!(

    pub fn leave_alternate_screen(&mut self) {
        let _ = execute!(

    pub fn clear_screen(&mut self) {
        let _ = self.terminal.clear();

impl Drop for Context {
    fn drop(&mut self) {
        // Re-enable terminal stuff
        let _ = disable_raw_mode();

// Let's create the model

struct Model {
    quit: bool,
    redraw: bool,
    view: View,

fn main() {
    let mut ctx: Context = Context::new();
    // We need to setup the terminal, entering alternate screen
    // Let's create a View
    let mut myview: View = View::init();
    // Let's mount all the components we need
                .with_label(String::from("Input component"))
    // ...
    // Give focus to our component;
    // Prepare states
    let mut states: Model = Model { quit: false, redraw: true, view: myview, };
    // Loop until states.quit is false
    while !states.quit {
         // Listen for input events
         if let Ok(Some(ev)) = ctx.input_hnd.read_event() {
            // Pass event to view
            let msg = myview.on(ev);
            states.redraw = true;
            // Call the elm-like update
            update(&mut states, &mut myview, msg);
        // If redraw, draw interface
        if states.redraw {
            // Call the elm elm-like vie1 function
            view(&mut ctx, &myview);
            states.redraw = false;
    // Finalize context

// -- view

fn view(ctx: &mut Context, view: &View) {
    let _ = ctx.terminal.draw(|f| {
        // Prepare chunks
        let chunks = Layout::default()
        view.render(MY_COMPONENT, f, chunks[0]);

// -- update

impl Update for Model {
    fn update(&mut self, msg: Option<(String, Msg)>) -> Option<(String, Msg)> {
        let ref_msg: Option<(&str, &Msg)> = msg.as_ref().map(|(s, msg)| (s.as_str(), msg));
        match ref_msg {
            None => None, // Exit after None
            Some(msg) => match msg {
                (COMPONENT_INPUT, Msg::OnChange(Payload::One(Value::Str(input)))) => {
                    eprintln!("User submitted: {}", input);
                (_, &MSG_KEY_ESC) => {
                    // Quit on esc
                    self.quit = true;
                _ => None,


See the examples directory to check out how to setup tui-realm. If you want a real in-production implementation, check out my project termscp


The update function is used to handle the messages returned from the view. The name update is just a convention, you can call this function as you prefer. I suggest you to use update, though, since it’s like in Elm. The usual signature of this function is fn update(model: &mut MyModel, msg: Option<(String, Msg)>) -> Option<(String, Msg)> We don’t have to mind what model is right now, it may be everything, probably it contains the View and some states, who cares; what is important here is the msg. The message is a tuple made up by a String, which is the identifier of the component which raised the event and a Msg enum, which contains the message itself. What can we do with this then? Well, we can make a super-cool match case where we match the tuple (ID, Msg) and for each branch, we can make everything we want. And as you’ve probably already noticed this function returns a Message tuple too. This should already have triggered you! Yes, you can call this function recursively and chain different events. Just a friendly reminder: you can obviously match only the event or only the ID of the component in the match case, just mind of the top-bottom priority ;)

Match Keys in Update

If you’re in trouble handling key events, you’re not a bad dev, it happened to me too the first time. To handle them, my suggestion is to create a keymap module where you store as const all the key you need. Just follow this example:

extern crate crossterm;
extern crate tuirealm;

use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use tuirealm::Msg;

// -- keys

pub const MSG_KEY_ESC: Msg = Msg::OnKey(KeyEvent {
    code: KeyCode::Esc,
    modifiers: KeyModifiers::NONE,

pub const MSG_KEY_TAB: Msg = Msg::OnKey(KeyEvent {
    code: KeyCode::Tab,
    modifiers: KeyModifiers::NONE,

Then you’ll be able to match them as in the examples: (MY_COMPONENT, &MSG_KEY_TAB) => {...}


Components represents a logical layer above a tui widget. Components allows you to handle properties and states for widgets and this will be rendered. The component is implemented through a trait which is called Component indeed. For each component you must implement these methods:

  • render: this method renders the component inside the area passed as argument. Mind that the component should be rendered only if props.visible is true.
  • update: update the component properties. You can return a Msg if you want and change the states if needed.
  • get_props: returns a copy of the component properties
  • on: handle an input event; returns a Msg
  • get_state: returns the current state to be exposed to the user.
  • blur: change the focus state for the component. This won’t affect the view.
  • active: enable the focus state for the component. This won’t affect the view.

Each component should have properties, provided by the Props struct and, if required some states, which are defined for each struct. States mustn’t be public. I strongly suggest to implement a PropsBuilder, via the namesake trait, for each component properties, in order to make easier to define properties for each component, while if you don’t want to do so, you can use the GenericPropsBuilder.


pub use self::props::borders;
pub use self::props::texts;
pub use self::props::GenericPropsBuilder;
pub use self::props::InputType;
pub use self::props::PropPayload;
pub use self::props::PropValue;
pub use self::props::Props;
pub use self::props::PropsBuilder;
pub use self::view::View;






















Type Definitions