panes 0.18.4

Renderer-agnostic layout engine with declarative ergonomics
Documentation

crates.io docs.rs CI downloads dependencies license

You keep rebuilding the same pane manager in every app: split views, stacks, dashboards, resize handles, focus order, and panel insertion rules.

panes is a pane layout and runtime engine. It computes panel rectangles without rendering them, so the same layout logic can power ratatui, egui, the browser, canvas, wasm, or your own renderer.

Describe panels in rows, columns, and tiling presets. panes solves the geometry via Taffy, tracks runtime state like focus and mutations, and hands back a map of PanelId -> Rect. No widget system. No renderer lock-in.

Use it when you need more than static flexbox: split-pane apps, IDE-like shells, dashboards, game HUDs, TUI workspaces, or any UI where panels are added, removed, resized, focused, or restored from snapshots.

Proof

use panes::{layout, grow, fixed};

// Game HUD: health bar pinned at 40px, viewport fills the rest
let layout = layout! {
    col {
        row {
            panel("viewport", grow: 1.0)
        }
        row(gap: 4.0) {
            panel("health", fixed: 40.0)
            panel("inventory", grow: 1.0)
            panel("minimap", fixed: 48.0)
        }
    }
}?;

let resolved = layout.resolve(800.0, 600.0)?;
for entry in resolved.panels() {
    println!("{}: {:?}", entry.kind, entry.rect);
}

Install

cargo add panes

Optional features:

Feature What it enables
serde Serialize/Deserialize on Rect, PanelId, NodeId
toml Load layouts from TOML strings/files (implies serde)

Usage

Build custom layouts or pick from 13 presets. Pass any coordinate system: pixels, logical points, terminal cells.

// Custom — full control with the layout macro
let layout = layout! {
    row(gap: 8.0) {
        panel("editor", grow: 2.0)
        col {
            panel("chat")
            panel("status", fixed: 3.0)
        }
    }
}?;
// Preset — one-liner for common patterns
Layout::master_stack(["editor", "chat", "status"]).master_ratio(0.6).gap(1.0)
// Runtime — add/remove panels, focus navigation, frame diffing
let mut rt = Layout::master_stack(["editor", "chat", "status"])
    .master_ratio(0.6).gap(1.0).into_runtime()?;

rt.add_panel("terminal".into())?;          // insert after focused, rebuild via strategy
rt.add_panel_with("logs".into(), Placement::End)?;  // append to end
rt.swap_next();                            // reorder in sequence
rt.focus_next();
rt.focus_direction_current(FocusDirection::Right)?;

let frame = rt.resolve(80.0, 24.0)?;
let diff = rt.last_diff();  // added, removed, moved, resized, unchanged

// Snapshot — save session state, restore later
let snapshot = rt.snapshot()?;
// serde_json::to_string(&snapshot)?;  // with `serde` feature
let mut rt2 = LayoutRuntime::from_snapshot(snapshot)?;

Adapters convert rects to renderer-native types:

Crate Target Rect type
panes-ratatui ratatui ratatui::layout::Rect (u16, edge-rounded)
panes-egui egui egui::Rect (f32)
panes-css Browser CSS declarations (browser solves layout)
panes-wasm Canvas/JS WasmRect (f64)

See the demo app for a working ratatui example, or try the live wasm demo.

For ratatui, panes_ratatui::resolve() handles the f32→u16 terminal cell conversion automatically — no manual quantization:

terminal.draw(|frame| {
    let panes = panes_ratatui::resolve(&mut rt, frame.area())?;
    for entry in panes.panels() {
        frame.render_widget(make_widget(entry.kind), entry.rect);
    }
    for (entry, is_focused) in panes.focused_panels(rt.focused()) {
        let style = if is_focused { highlight } else { normal };
        frame.render_widget(styled_block(entry.kind, style), entry.rect);
    }
})?;

For other adapters, panels() yields PanelEntry { id, kind, rect, kind_index } with native rect types — no hashmap, no cross-referencing.

Performance

LayoutRuntime resolves a 100-panel flat layout in <1µs on the hot path — roughly 3x over raw Taffy. The abstraction layer (tree compilation, rect resolution, frame diffing) adds minimal overhead.

See benches/ for methodology.

Documentation

See the User Guide for the full API: all 13 presets, the layout macro, TOML configuration, runtime mutations, frame diffing, animation, and render adapters.

License

MIT or Apache 2.0, at your option. See LICENSE-MIT and LICENSE-APACHE.