#terminal-colors #styling #template

standout-render

Styled terminal rendering with templates, themes, and adaptive color support

19 stable releases (5 major)

new 7.0.0 Feb 17, 2026
6.2.0 Feb 15, 2026
5.0.0 Feb 3, 2026
4.0.0 Feb 2, 2026
2.1.0 Jan 18, 2026

#459 in Template engine

Download history 14/week @ 2026-01-26 11/week @ 2026-02-02 93/week @ 2026-02-09

118 downloads per month
Used in 3 crates (via standout)

MIT license

1MB
17K SLoC

standout-render

Rich terminal output with templates, themes, and automatic light/dark mode support.

use standout_render::{render, Theme};
use console::Style;

let theme = Theme::new()
    .add("title", Style::new().cyan().bold())
    .add("count", Style::new().yellow());

let output = render(
    "[title]{{ name }}[/title]: [count]{{ count }}[/count] items",
    &json!({"name": "Tasks", "count": 42}),
    &theme,
)?;

Why standout-render?

Terminal output is stuck in the 1970s: scattered println! statements, cryptic ANSI escape codes, and presentation logic tangled with business logic. Every change requires recompilation. Nobody bothers with polish because iteration is painful.

standout-render fixes this with ideas borrowed from web development:

  • Templates — MiniJinja (Jinja2 syntax) for readable, declarative output
  • Style tags — BBCode-like [style]content[/style] syntax, not escape codes
  • Themes — Centralized style definitions in CSS or YAML
  • Hot reload — Edit templates during development, see changes instantly
  • Graceful degradation — Same template renders rich or plain based on terminal

The result: output that's easy to write, easy to change, and looks polished.

Features

Two-Pass Rendering Pipeline

Template + Data → MiniJinja → Text with style tags → BBParser → ANSI output
  1. Pass 1: Variable substitution, loops, conditionals (MiniJinja)
  2. Pass 2: Style tag replacement with ANSI codes (or stripping for plain text)

Adaptive Themes

Automatic OS detection for light/dark terminals:

# theme.yaml
title:
  fg: cyan
  bold: true
panel:
  light:
    bg: "#f5f5f5"
  dark:
    bg: "#1a1a1a"

Or CSS syntax:

.title { color: cyan; font-weight: bold; }

@media (prefers-color-scheme: dark) {
    .panel { background: #1a1a1a; }
}

Multiple Output Modes

One template, many formats:

// Rich terminal output
render_with_output(template, &data, &theme, OutputMode::Term)?;

// Plain text (pipes, redirects)
render_with_output(template, &data, &theme, OutputMode::Text)?;

// Structured data (skip template entirely)
render_auto(template, &data, &theme, OutputMode::Json)?;
render_auto(template, &data, &theme, OutputMode::Yaml)?;

Tabular Layout

Sophisticated column formatting with world-class alignment and layout:

{% set t = tabular([
    {"name": "id", "width": 8},
    {"name": "name", "width": "fill"},
    {"name": "status", "width": 10, "anchor": "right"}
], separator="  ") %}
{% for entry in entries %}
{{ t.row([entry.id, entry.name, entry.status]) }}
{% endfor %}

Features: flexible truncation (start/middle/end), expanding columns, word wrapping, multi-line row alignment, justification, variable width, fractional sizing—all Unicode-aware and ANSI-safe.

Hot Reload

During development, templates reload from disk on each render:

let mut renderer = Renderer::new(theme)?;
renderer.add_template_dir("./templates")?;

// Edit templates/report.jinja, re-run, see changes immediately
let output = renderer.render("report", &data)?;

In release builds, templates embed into the binary—no runtime file access.

Quick Start

[dependencies]
standout-render = "2.1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
use standout_render::{render, Theme};
use serde::Serialize;

#[derive(Serialize)]
struct Report { title: String, items: Vec<String> }

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let theme = Theme::from_yaml(r#"
        title: { fg: cyan, bold: true }
        item: green
        count: { dim: true }
    "#)?;

    let report = Report {
        title: "Status".into(),
        items: vec!["server-1".into(), "server-2".into()],
    };

    let template = r#"
[title]{{ title }}[/title]
{% for item in items %}
  [item]{{ item }}[/item]
{% endfor %}
[count]{{ items | length }} items[/count]
"#;

    let output = render(template, &report, &theme)?;
    print!("{}", output);
    Ok(())
}

Documentation

Guides

Topics

Reference

Used By

This crate provides the rendering foundation for the standout CLI framework, which adds command dispatch, hooks, and clap integration. Use standout-render directly when you want rendering without the framework.

License

MIT

Dependencies

~7–27MB
~344K SLoC