Skip to main content
An ingredient is one preview: a specific widget configuration with mock data. You implement the Ingredient trait for a struct, and the Pantry renders it in the preview pane with navigation, description, and prop documentation.

The Ingredient trait

Four methods are required:
MethodPurpose
group()Widget name, shown as a collapsible heading in the sidebar. Shared values nest together.
name()Variant label, the leaf node under the group.
source()Module path shown as a breadcrumb in the preview pane.
render()Draw the widget into the preview area.
Optional methods with defaults:
MethodDefaultPurpose
tab()"Widgets"Top-level tab: "Widgets", "Panes", "Views", or "Styles".
description()""One-line summary displayed in the preview pane.
props()&[]PropInfo slice documenting the widget’s configurable surface.
interactive()falseWhether the preview captures keyboard and mouse input.
animated()falseWhether the ingredient needs periodic redraws (33ms tick).
handle_key()falseProcess a key event while focused.
handle_mouse()falseProcess a mouse event while focused.

Basic ingredient

A static ingredient is a unit struct with mock data:
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
use tui_pantry::{Ingredient, PropInfo};

struct GaugeDefault;

impl Ingredient for GaugeDefault {
    fn group(&self) -> &str { "Resource Gauge" }
    fn name(&self) -> &str { "Default" }
    fn source(&self) -> &str { "my_crate::widgets::resource_gauge" }

    fn description(&self) -> &str {
        "Horizontal bar showing resource utilization with color thresholds"
    }

    fn props(&self) -> &[PropInfo] {
        &[
            PropInfo { name: "label", ty: "&str", description: "Resource name" },
            PropInfo { name: "ratio", ty: "f64", description: "Fill from 0.0 to 1.0" },
        ]
    }

    fn render(&self, area: Rect, buf: &mut Buffer) {
        ResourceGauge::new("CPU", 0.34).render(area, buf);
    }
}
Each ingredient module exports a factory function:
pub fn ingredients() -> Vec<Box<dyn Ingredient>> {
    vec![Box::new(GaugeDefault), Box::new(GaugeHigh)]
}

Interactive ingredient

Set interactive() to true to receive keyboard and mouse input when the preview pane has focus. Press Enter in the sidebar to focus an interactive ingredient. Press Esc to return.
struct TableInteractive {
    selected: usize,
}

impl Ingredient for TableInteractive {
    fn group(&self) -> &str { "Node Table" }
    fn name(&self) -> &str { "Interactive" }
    fn source(&self) -> &str { "my_crate::widgets::node_table" }

    fn interactive(&self) -> bool { true }

    fn handle_key(&mut self, code: KeyCode) -> bool {
        match code {
            KeyCode::Up => { self.selected = self.selected.saturating_sub(1); true }
            KeyCode::Down => { self.selected += 1; true }
            _ => false,
        }
    }

    fn render(&self, area: Rect, buf: &mut Buffer) {
        NodeTable::new(Some(self.selected)).render(area, buf);
    }
}
Return true from handle_key() or handle_mouse() to consume the event, false to let the Pantry handle it.

Tab assignment

Override tab() to place ingredients in a different tab:
fn tab(&self) -> &str { "Panes" }   // composed sections
fn tab(&self) -> &str { "Views" }   // full-page layouts

Feature gating

Gate ingredient modules behind #[cfg(feature = "tui-pantry")] so they don’t compile into production:
// widgets/gauge/mod.rs
#[cfg(feature = "tui-pantry")]
#[path = "gauge.ingredient.rs"]
pub mod ingredient;
For flat crates, gate at the crate root:
// lib.rs
#[cfg(feature = "tui-pantry")]
pub mod ingredient;

Registration

Declare your ingredient modules in pantry.toml:
[ingredients]
source = "my_crate"
modules = [
    "widgets::gauge",
    "widgets::node_table",
]
Each entry expands at compile time to my_crate::widgets::gauge::ingredient::ingredients() via the pantry_ingredients!() proc macro. The entry point uses this automatically:
// examples/widget_preview/main.rs
fn main() -> std::io::Result<()> {
    tui_pantry::run!()
}
For crates without an [ingredients] section in pantry.toml, pass the factory directly:
fn main() -> std::io::Result<()> {
    tui_pantry::run!(my_crate::ingredient::ingredients())
}
For multi-crate workspaces, see the array-of-tables syntax in the configuration reference.

Example pantry

The TUI Pantry repository includes a complete example pantry showcasing ratatui’s stock widgets with a Catppuccin Mocha theme. It demonstrates all four tabs (Widgets, Panes, Views, and Styles) and serves as a reference for the full integration pattern. Run it with:
cargo run -p example-pantry