Skip to main content
ansicode
Rust

ANSI escape codes in Rust

Rust string literals support `\x1b` directly — `println!("\x1b[31mred\x1b[0m")` works with no dependencies. For ergonomic colour APIs reach for `owo-colors` (zero-allocation, compile-time) or `colored` (more dynamic). For NO_COLOR / isatty handling without writing the env-check yourself, `termcolor` mirrors the Go pattern. For full TUI work — windows, cursor, raw mode, mouse — use `crossterm`; for higher-level layouts, `ratatui` (formerly tui-rs) sits on top of it.

Recommended libraries

  • owo-colors

    Zero-allocation colour extension on every string-like type — `"error".red().bold()` produces no heap traffic. Compile-time style colors via const generics. ANSI only; no terminfo lookup.

  • colored

    The original `"text".red().bold()` API — more flexible than owo-colors (runtime palette, custom colours) at the cost of allocation. Honours `NO_COLOR` and `CLICOLOR_FORCE` out of the box.

  • termcolor

    ripgrep's colour layer — a `WriteColor` trait over stdout/stderr that handles ANSI on POSIX and the Windows console API on Windows. `ColorChoice::Auto` does NO_COLOR + isatty for you.

  • crossterm

    Full cross-platform terminal toolkit — cursor, colour, raw mode, alternate screen, mouse / focus / paste events, keyboard input. The foundation under `ratatui`. Use directly for CLIs; layer ratatui for full-screen TUIs.

Idiomatic patterns

Direct println! with literal escapes
fn main() {
    println!("\x1b[1;31merror:\x1b[0m permission denied");
}
Ergonomic colours with owo-colors
use owo_colors::OwoColorize;

fn main() {
    println!("{} permission denied", "error:".red().bold());
}
NO_COLOR + isatty via termcolor
use std::io::Write;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

fn main() -> std::io::Result<()> {
    // Auto downgrades to plain text under NO_COLOR= or non-tty stdout.
    let mut out = StandardStream::stdout(ColorChoice::Auto);
    out.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
    write!(out, "error:")?;
    out.reset()?;
    writeln!(out, " permission denied")?;
    Ok(())
}
Raw mode + cursor hide with crossterm
use std::io::{stdout, Write};
use crossterm::{
    cursor::{Hide, Show},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode},
};

fn main() -> std::io::Result<()> {
    enable_raw_mode()?;
    execute!(stdout(), Hide)?;
    // ... read keys, render UI ...
    execute!(stdout(), Show)?;          // always restore
    disable_raw_mode()?;
    Ok(())
}

Related sequences

Other languages