Rich Output
Rich renders formatted text, tables, progress bars, and syntax highlighting in the terminal. It makes CLI output readable without sacrificing scriptability.
Recipe
uv add richfrom rich.console import Console
console = Console()
console.print("[bold green]Success![/bold green] Deployed v1.2.3")When to reach for this:
- Human-facing CLI output (tables, status dashboards)
- Long-running operations needing progress bars
- Syntax-highlighted config or log display
Working Example
from rich.console import Console
from rich.table import Table
from rich.progress import track
from rich.syntax import Syntax
import time
console = Console()
def show_users(users: list[dict]):
table = Table(title="Active Users")
table.add_column("ID", style="cyan", justify="right")
table.add_column("Email")
table.add_column("Role", style="magenta")
for u in users:
table.add_row(str(u["id"]), u["email"], u["role"])
console.print(table)
def process_files(paths: list[str]):
for path in track(paths, description="Processing..."):
time.sleep(0.1) # simulate work
console.print("[green]Done![/green]")
def show_config(yaml_text: str):
syntax = Syntax(yaml_text, "yaml", theme="monokai", line_numbers=True)
console.print(syntax)What this demonstrates:
- Tables with styled columns for structured data
track()wraps iterables with a progress barSyntaxhighlights code and config files- Markup
[green]for inline styling
Deep Dive
Output Modes
| Mode | Use |
|---|---|
| Rich table | Human inspection |
| JSON to stdout | Machine parsing (--json flag) |
| stderr for logs | Keep stdout pipeable |
Console(soft_wrap=True) | Narrow terminals |
Gotchas
- Rich output in piped commands - ANSI codes in files. Fix:
Console(force_terminal=False)or--plainflag. - Progress bar on stderr - correct default. Fix: do not redirect progress to stdout.
- Tables for script consumption - breaks parsing. Fix:
--jsonflag for machine output; Rich for default human mode. - Logging via print - no levels or timestamps. Fix:
rich.logging.RichHandlerfor structured logs. - Huge tables - terminal overflow. Fix: paginate or limit rows with
--limitflag.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Plain print | Scriptable output only | User-facing dashboard |
| tabulate | Simple tables, no colors | Progress bars needed |
| tqdm | Progress only | Full formatting suite |
FAQs
Rich or plain output?
Rich for interactive human use. Add --json or --plain for automation.
Does Rich work on Windows?
Yes. Windows Terminal supports ANSI colors. Legacy cmd may need colorama.
How do I log with Rich?
logging.basicConfig(handler=RichHandler(), level=logging.INFO).
Can Rich render Markdown?
from rich.markdown import Markdown; console.print(Markdown(text)).
How do I test Rich output?
Console(file=StringIO()) captures output for assertions.
Live dashboards?
from rich.live import Live for updating displays.
Rich in Click/Typer?
Compatible. Use click.echo for simple text; Rich Console for complex output.
How do I disable colors?
Console(no_color=True) or NO_COLOR=1 env var.
Tree views?
from rich.tree import Tree for hierarchical display.
Performance on large output?
Stream or paginate. Do not build million-row tables.
Related
- CLI Basics - stdout/stderr conventions
- Click - CLI framework integration
- Interactive Prompts - user input
- CLI Best Practices - output guidelines
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.