Skip to content

Tutorial: Building a Live Dashboard

This tutorial demonstrates how to build a dynamic, full-screen terminal dashboard using fast-rich. We will combine Layouts, Panels, Tables, and Live Display to create a monitoring tool interface.

1. The Goal

We want to create a UI with: * A Header (Top) * A Main Area split into: * Sidebar (Left, Menu) * Body (Right, Data Table) * A Footer (Bottom, Status) * Live Updates: The body content will update automatically.

2. Setting up the Layout

First, we define the static structure using Layout.

use fast_rich::prelude::*;

fn create_layout() -> fast_rich::layout::Layout {
    // Root layout (vertical stack)
    let mut layout = fast_rich::layout::Layout::new().with_name("root");

    // Split into Header (size 3), Main (auto), Footer (size 1)
    layout.split_column(vec![
        fast_rich::layout::Layout::new().with_name("header").with_size(3),
        fast_rich::layout::Layout::new().with_name("main").with_ratio(1),
        fast_rich::layout::Layout::new().with_name("footer").with_size(1),
    ]);

    // Split "main" into Sidebar (ratio 1) and Body (ratio 3)
    // Note: We access children by index. 0=header, 1=main, 2=footer.
    layout.children_mut()[1].split_row(vec![
        fast_rich::layout::Layout::new().with_name("sidebar").with_ratio(1),
        fast_rich::layout::Layout::new().with_name("body").with_ratio(3),
    ]);

    layout
}

3. Creating Content Components

We can use helper functions to generate the content (Renderables) for each section.

fn get_header() -> Panel {
    Panel::new(
        Text::from("Server Monitor v1.0")
            .alignment(Alignment::Center)
            .style(Style::new().bold().foreground(Color::White).background(Color::Blue))
    )
    .border_style(BorderStyle::Heavy)
}

fn get_sidebar() -> Panel {
    Panel::new(
        Text::from("Dashboard\nProcesses\nNetwork\nDisk\nSettings")
    )
    .title("Menu")
}

fn get_footer() -> Text {
    Text::from("Status: Online | Press Ctrl+C to exit")
        .style(Style::new().dim())
        .alignment(Alignment::Center)
}

fn get_body_table(tick: u64) -> Panel {
    let mut table = Table::new();
    table.add_column("PID");
    table.add_column("Process");
    table.add_column("CPU %");
    table.add_column("Mem %");

    // Simulate changing data
    let cpu_load = (tick * 7) % 100;
    table.add_row_strs(&["1023", "fast-rich-demo", &format!("{}", cpu_load), "1.2"]);
    table.add_row_strs(&["4096", "cargo", "0.0", "0.5"]);
    table.add_row_strs(&["8888", "postgres", "3.4", "12.0"]);

    Panel::new(table)
        .title("Active Processes")
        .border_style(BorderStyle::Rounded)
}

4. Bringing it to Live

Now we use Live to manage the render loop.

use std::thread;
use std::time::Duration;

fn main() {
    let mut layout = create_layout();

    // Fill static parts immediately
    layout.children_mut()[0].update(get_header());
    layout.children_mut()[2].update(get_footer());

    // Access main -> sidebar
    layout.children_mut()[1].children_mut()[0].update(get_sidebar());

    // Start Live display
    let mut live = fast_rich::live::Live::new();
    live.start().unwrap();

    for tick in 0..100 {
        // Update the body (dynamic part)
        // main (idx 1) -> body (idx 1)
        let body_content = get_body_table(tick);
        layout.children_mut()[1].children_mut()[1].update(body_content);

        // Update the live display with the full layout tree
        live.update(layout.clone()); // Cloning layout is cheap (Arc internally for renderables)
        live.refresh().unwrap();

        thread::sleep(Duration::from_millis(200));
    }

    live.stop().unwrap();
}

Summary

In this tutorial, you learned how to: 1. Structure a complex terminal UI using recursive Layouts. 2. Use Panel and Table to organize information. 3. Use Live to animate the dashboard without screen flickering.

Check out examples/layout_demo.rs and examples/live_table.rs in the repository for runnable code!