Rich Output: How to Format Beautiful CLI Text & Tables
Plain text CLIs feel dated. The Rich library makes CLI output professional and readable with colors, tables, panels, and formatted text. Rich integrates seamlessly with Typer and outputs beautiful, responsive formatting that adapts to terminal width automatically.
What is Rich and Why Use It?
Rich is a Python library for rendering rich text and beautiful formatting in terminals. According to the Rich GitHub repository (2026), it's used by tools like Typer itself, pip, and dozens of popular CLI tools. Rich handles Unicode, colors, layouts, and automatically falls back to plain text on terminals that don't support formatting, ensuring compatibility everywhere.
Installing Rich
Install Rich alongside Typer:
pip install rich
Rich is a standalone library that works with any Python program. It requires no additional setup and outputs formatting compatible with most terminals.
Colored Text Output
Add colors to your CLI output to emphasize important information:
import typer
from rich import print
app = typer.Typer()
@app.command()
def deploy(service: str):
"""Deploy a service."""
print(f"[green]✓[/green] Deploying {service}")
print(f"[yellow]⚠[/yellow] This will interrupt service briefly")
print(f"[red]✗[/red] Rollback available if deployment fails")
if __name__ == "__main__":
app()
Rich uses markup tags like [green]...[/green] for colors. Available colors include red, green, yellow, blue, magenta, cyan, and many more. This output is readable and conveys meaning at a glance.
Building Tables
Tables organize data beautifully:
import typer
from rich.table import Table
from rich import print
app = typer.Typer()
@app.command()
def list_services():
"""List all services and their status."""
table = Table(title="Services")
table.add_column("Service", style="cyan")
table.add_column("Status", style="magenta")
table.add_column("CPU %", justify="right", style="green")
table.add_row("web-api", "Running", "23.5")
table.add_row("worker-1", "Running", "15.2")
table.add_row("worker-2", "Stopped", "0.0")
print(table)
if __name__ == "__main__":
app()
Output will be a nicely formatted table with colors and alignment. Rich handles column widths automatically based on terminal size.
Panels and Alerts
Highlight important information with panels:
import typer
from rich.panel import Panel
from rich.console import Console
console = Console()
app = typer.Typer()
@app.command()
def backup_status():
"""Show backup status."""
console.print(
Panel(
"[green]Backup completed successfully[/green]\n"
"Size: 2.4 GB\n"
"Duration: 4m 32s",
title="Backup Report",
border_style="green",
)
)
console.print(
Panel(
"[red]This database is 6 months old[/red]\n"
"Consider running refresh_data --full",
title="Warning",
border_style="red",
)
)
if __name__ == "__main__":
app()
Panels draw a border around content, making alerts stand out. border_style controls the color of the border.
Formatted Output with Syntax Highlighting
Display code or formatted text with syntax highlighting:
import typer
from rich.console import Console
from rich.syntax import Syntax
import json
console = Console()
app = typer.Typer()
@app.command()
def show_config():
"""Display configuration file."""
config = {
"api_url": "https://api.example.com",
"timeout": 30,
"retries": 3,
}
syntax = Syntax(
json.dumps(config, indent=2),
"json",
theme="monokai",
line_numbers=True,
)
console.print(syntax)
if __name__ == "__main__":
app()
Syntax highlighting makes config files, logs, and code snippets readable in the terminal. Rich supports dozens of languages and color themes.
Progress Tracking
Show progress for long-running operations:
import typer
from rich.progress import Progress
import time
app = typer.Typer()
@app.command()
def process_files(count: int = 100):
"""Process multiple files."""
with Progress() as progress:
task = progress.add_task("[cyan]Processing...", total=count)
for i in range(count):
time.sleep(0.01) # Simulate work
progress.update(task, advance=1)
typer.echo("Done!")
if __name__ == "__main__":
app()
The progress bar updates in real-time, showing current progress, rate, and estimated time remaining. This is covered in depth in article 8.
Layout with Columns
Display content side-by-side:
import typer
from rich.console import Console
from rich.columns import Columns
from rich.panel import Panel
console = Console()
app = typer.Typer()
@app.command()
def compare_versions():
"""Compare two versions."""
old = Panel("[red]Version 1.0[/red]\n- Feature A\n- Feature B", title="Old")
new = Panel("[green]Version 2.0[/green]\n- Feature A\n- Feature B\n- Feature C", title="New")
console.print(Columns([old, new]))
if __name__ == "__main__":
app()
Columns automatically adjust to available terminal width, stacking vertically on narrow terminals.
Comparison Table: Output Approaches
| Method | Use Case | Effort | Output Quality |
|---|---|---|---|
print() / typer.echo() | Simple text | Minimal | Basic |
| Rich markup text | Colored messages | Low | Good |
| Rich tables | Data display | Low-Medium | Excellent |
| Rich panels | Alerts, emphasis | Low-Medium | Excellent |
| Rich syntax | Code, configs | Medium | Professional |
| Rich progress | Long operations | Medium | Professional |
Key Takeaways
- Rich library provides markup-based coloring with
[color]text[/color]syntax. - Tables organize tabular data with automatic column sizing and alignment.
- Panels create bordered sections perfect for alerts and important information.
- Syntax highlighting makes code and config output professional and readable.
- Progress bars show real-time operation status for long-running tasks.
- Rich automatically degrades gracefully on terminals without color support.
Frequently Asked Questions
Does Rich work on Windows?
Yes. Rich detects Windows and enables VT100 escape sequences automatically on Windows 10+. For older Windows versions, output degrades to plain text.
Can I use Rich with Typer?
Yes. Import Rich's print() function in your Typer commands and use it directly. Typer and Rich are fully compatible.
How do I customize table colors and styling?
Use the style parameter on add_column() and add_row() with color names or hex codes. You can also set style on individual cells by using Rich's Cell objects.
What if my terminal is very narrow?
Rich automatically wraps text and adjusts layouts. Tables stack vertically, columns reflow, and panels shrink gracefully. Test with rich by resizing your terminal to verify responsiveness.
Can I export Rich output to a file?
Yes. Create a Console with file=open('output.txt', 'w') and all output goes to the file. Use force_terminal=True to force ANSI color codes in files.