Colored Terminal Output in Rust With Anstyle and Owo Colors
Add colors to CLI output with anstyle or owo-colors, and turn them off when stdout is not a terminal or NO_COLOR is set, so pipes and logs stay clean.

Color helps a person find an error in forty lines of output and ruins a log file that captured the escape codes. The trick is not choosing colors but deciding when to print them. This post uses anstyle for the styles and anstream for the decision, the same pair that clap uses internally. It is a polish step in the command line tool series.
anstyle 1, anstream 0.6, Rust 1.90.
Styles and the stream
[dependencies]
anstyle = "1"
anstream = "0.6"use anstream::println;
use anstyle::{AnsiColor, Style};
fn main() {
let ok = Style::new().fg_color(Some(AnsiColor::Green.into())).bold();
let bad = Style::new().fg_color(Some(AnsiColor::Red.into()));
println!("{ok}passed{ok:#} 12 tests");
println!("{bad}failed{bad:#} 1 test");
}{ok} writes the escape sequence to start the style, and {ok:#} writes the reset. Because println! here is from anstream, running cargo run | cat prints plain passed 12 tests. In a terminal you get green bold text. The same program writing with std::println! would put raw escapes into the pipe.
Why not decide yourself
The checks involve several signals:
- Is the stream a terminal? (
std::io::IsTerminal) - Is
NO_COLORset to a non-empty value? Then disable. - Is
CLICOLOR_FORCEset? Then enable even when piped. - Is
TERM=dumb? Disable. - Is it Windows? Enable virtual terminal processing.
anstream implements this list. Rolling your own usually gets one of them wrong.
owo-colors for inline use
use owo_colors::OwoColorize;
println!("{}", "error".red().bold());It reads nicely, but it always emits the codes. Pair it with owo_colors::set_override or a check on IsTerminal, or use its supports-colors feature.
A --color flag
#[derive(clap::ValueEnum, Clone, Copy)]
enum ColorMode { Auto, Always, Never }Map it to anstream::ColorChoice, and call choice.write_global() at startup. Clap already follows the same environment variables for its own output, so NO_COLOR affects help text without extra code.
Rules of thumb
- Data on stdout stays plain unless the user asks otherwise.
- Errors on stderr may be colored, since people read them directly.
- Never rely on color alone: include words such as
error:andwarning:. - Test with
| catbefore you ship.
When to skip color
Machine readable formats (JSON, CSV) never get styles. Logging libraries handle their own colors, as in structured logging with tracing. The conventions are documented at no-color.org, and the crate at anstream on docs.rs.


