Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Rich — User Guide

Generated May 2026 from Textualize/rich at 46cebbb

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Rich is a Python library for rich text and beautiful formatting in the terminal. It provides an extensive toolkit for building polished command-line interfaces: styled text with colors and formatting, tables with borders and alignment, progress bars, syntax highlighting, Markdown rendering, pretty-printed data structures, enhanced tracebacks, tree views, panels, layouts, live-updating displays, logging integration, and more. Rich works with any terminal that supports ANSI escape codes and automatically adapts its output to the capabilities of the environment -- from 4-bit color all the way to 24-bit truecolor, with automatic downgrade for limited terminals.


1. Quick Start

Get Rich installed and printing styled output in under a minute.

Install and verify

pip install rich

Verify the installation by running the built-in test card, which demonstrates most of Rich's features in a single output:

python -m rich

You should see a colorful display showcasing colors, styles, text justification, markup, tables, syntax highlighting, pretty printing, Markdown, and more.

Your first Rich print

from rich import print

print("Hello, [bold magenta]World[/bold magenta]!")

That single import replaces Python's built-in print() with Rich's version, which understands BBCode-like markup for inline styling. The output renders "World" in bold magenta.

A quick table

from rich.console import Console
from rich.table import Table

table = Table(title="Grocery List")
table.add_column("Item", style="cyan")
table.add_column("Qty", justify="right", style="green")

table.add_row("Apples", "6")
table.add_row("Bread", "1")
table.add_row("Milk", "2")

console = Console()
console.print(table)

Tip

Run python -m rich at any time to see a comprehensive demo of Rich's capabilities. It is the fastest way to confirm your terminal supports Rich output and to see what is available.


2. Installation

Requirements

Requirement Detail
Python 3.8 or later
OS Linux, macOS, Windows (Windows Terminal recommended)
Dependencies pygments (syntax highlighting), markdown-it-py (Markdown parsing) -- installed automatically by pip

Install from PyPI

pip install rich

Or with your preferred package manager:

# Poetry
poetry add rich

# Conda
conda install -c conda-forge rich

# pipx (for CLI utilities that use Rich)
pipx install rich

Development install

git clone https://github.com/Textualize/rich.git
cd rich
pip install -e ".[dev]"

Important

On Windows, use Windows Terminal for the best experience. The legacy cmd.exe console has limited color support. Rich auto-detects terminal capabilities and degrades gracefully, but colors and Unicode rendering will be reduced in older terminals.

Verify installation

>>> import rich
>>> rich.print("[green]Rich is working![/green]")

If you see green text, Rich is installed and your terminal supports styled output.


3. Configuration

Rich requires no configuration files. All behavior is controlled through Python constructor arguments and a small number of environment variables.

Environment variables

Variable Default Purpose
UNICODE_VERSION latest Override the Unicode version used for character width calculation. Set to a specific version (e.g., 13.0.0) if your terminal renders certain characters at unexpected widths.
DATABRICKS_RUNTIME_VERSION (unset) Automatically detected. When present, Rich adjusts its behavior for Databricks notebook environments.
NO_COLOR (unset) Industry standard. When set to any value, Rich disables all color output. See no-color.org.
TERM (varies) Rich reads the standard TERM variable to determine terminal capabilities. Setting TERM=dumb forces plain-text output.
COLUMNS (auto) Override the detected terminal width. Useful for CI or piped output.
FORCE_COLOR (unset) When set, forces Rich to output color even when the output is not a TTY.

Console configuration

The Console class is the central object in Rich. Most configuration happens at construction time:

from rich.console import Console

# Default: auto-detect everything
console = Console()

# Force a specific width (useful for testing or file output)
console = Console(width=120)

# Disable color
console = Console(no_color=True)

# Force terminal mode for non-TTY output (e.g., piping to a file)
console = Console(force_terminal=True)

# Write to stderr instead of stdout
console = Console(stderr=True)

# Use a custom file object
console = Console(file=open("output.txt", "w"))

# Enable recording for later export
console = Console(record=True)

Key Console constructor parameters:

Parameter Type Default Description
color_system str or None "auto" Force color system: "standard" (4-bit), "256" (8-bit), "truecolor" (24-bit), "windows", or None to disable
force_terminal bool False Treat output as a terminal even if not a TTY
force_jupyter bool False Force Jupyter notebook rendering
width int (auto) Override terminal width
height int (auto) Override terminal height
no_color bool False Disable all color
tab_size int 8 Number of spaces per tab
record bool False Enable output recording for export
markup bool True Enable Rich markup parsing
emoji bool True Enable emoji shortcode rendering
highlight bool True Enable automatic highlighting
log_time bool True Show timestamp in console.log()
log_path bool True Show file path in console.log()

Reconfiguring the global console

Rich maintains a global Console instance used by rich.print(). You can reconfigure it:

from rich import reconfigure

reconfigure(width=100, no_color=True)

Themes

Rich supports custom themes for consistent styling across your application:

from rich.console import Console
from rich.theme import Theme

custom_theme = Theme({
    "info": "dim cyan",
    "warning": "magenta",
    "danger": "bold red",
})

console = Console(theme=custom_theme)
console.print("This is [info]information[/info]")
console.print("This is [warning]a warning[/warning]")
console.print("This is [danger]dangerous[/danger]")

Themes can also be loaded from INI-style config files:

# mytheme.ini
[styles]
info = dim cyan
warning = magenta
danger = bold red
from rich.theme import Theme
theme = Theme.read("mytheme.ini")

4. Core Usage

4.1 Printing Styled Text

The rich.print() drop-in replacement

from rich import print

print("Hello, [bold red]World[/bold red]!")
print("[underline]Underlined[/underline] and [strike]strikethrough[/strike]")
print("[on blue]White on blue background[/on blue]")
print(":thumbs_up: Emoji support with shortcodes")

rich.print() has the same signature as the built-in print() -- sep, end, file, and flush all work as expected.

Rich markup syntax

Rich uses a BBCode-like markup for inline styling. Tags are enclosed in square brackets:

Markup Effect
[bold]text[/bold] Bold
[italic]text[/italic] Italic
[underline]text[/underline] Underlined
[strike]text[/strike] Strikethrough
[dim]text[/dim] Dimmed
[reverse]text[/reverse] Reversed foreground/background
[blink]text[/blink] Blinking (terminal support varies)
[red]text[/red] Named color
[#ff8800]text[/#ff8800] Hex color
[on blue]text[/on blue] Background color
[bold red on white]text[/] Combined styles, closed with [/]
[link=URL]text[/link] Clickable hyperlink (terminal support varies)

Tip

Use [/] as a shorthand to close all open tags. You can also use short forms: [b] for bold, [i] for italic, [u] for underline.

The Console class

For full control, use Console directly:

from rich.console import Console

console = Console()

# Basic printing with styles
console.print("Hello", style="bold green")

# Logging with timestamp and file path
console.log("Server started", style="info")

# Print a rule (horizontal line)
console.rule("[bold blue]Section Header")

# Print with justification
console.print("Centered text", justify="center")
console.print("Right-aligned", justify="right")

Text objects for fine-grained control

from rich.text import Text

text = Text("Hello, World!")
text.stylize("bold", 0, 5)           # "Hello" is bold
text.stylize("red", 7, 12)           # "World" is red
text.highlight_regex(r"\w+", "cyan") # Highlight all words

console.print(text)

You can also construct Text from markup:

text = Text.from_markup("[bold]Hello[/], [italic red]World[/]!")

Style objects

from rich.style import Style

warning_style = Style(bold=True, color="yellow", bgcolor="red")
console.print("WARNING!", style=warning_style)

# Parse from string
style = Style.parse("bold italic cyan on dark_blue")

Note

Rich supports 16 standard color names (red, green, blue, etc.), 256 extended colors via color(N) syntax, and 24-bit truecolor via rgb(R,G,B) or hex #RRGGBB. Rich automatically converts colors to the best available representation for the current terminal.

4.2 Tables

Rich renders tables with borders, colors, column alignment, and automatic sizing.

Basic table

from rich.console import Console
from rich.table import Table

table = Table(title="Star Wars Movies")

table.add_column("Released", style="cyan", no_wrap=True)
table.add_column("Title", style="magenta")
table.add_column("Box Office", justify="right", style="green")

table.add_row("Dec 20, 2019", "Star Wars: The Rise of Skywalker", "$952,110,690")
table.add_row("May 25, 2018", "Solo: A Star Wars Story", "$393,151,347")
table.add_row("Dec 15, 2017", "Star Wars Ep. VIII: The Last Jedi", "$1,332,539,889")
table.add_row("Dec 16, 2016", "Rogue One: A Star Wars Story", "$1,332,439,889")

console = Console()
console.print(table)

Table configuration

Parameter Type Default Description
title str None Title displayed above the table
caption str None Caption displayed below the table
box Box HEAVY_HEAD Border style (see rich.box module)
show_header bool True Show column headers
show_footer bool False Show column footers
show_edge bool True Show table border
show_lines bool False Show lines between rows
expand bool False Expand table to fill available width
padding int/tuple (0, 1) Cell padding
row_styles list None Alternating row styles, e.g. ["none", "dim"]

Column options

table.add_column(
    "Name",
    justify="left",      # "left", "center", "right", "full"
    style="cyan",         # Default cell style
    header_style="bold",  # Header cell style
    no_wrap=True,         # Prevent word wrapping
    ratio=2,              # Relative width ratio
    min_width=10,         # Minimum column width
    max_width=30,         # Maximum column width
    overflow="ellipsis",  # Overflow handling
)

Grid layout (borderless tables)

Table.grid() creates a borderless table useful for layouts:

table = Table.grid(padding=1, pad_edge=True)
table.add_column("Label", style="bold", justify="right")
table.add_column("Value")
table.add_row("Name:", "Rich")
table.add_row("Version:", "13.x")
table.add_row("Author:", "Will McGugan")
console.print(table)

Box styles

Rich includes many border styles in rich.box:

from rich import box

table = Table(box=box.ROUNDED)    # Rounded corners
table = Table(box=box.SIMPLE)     # Simple lines
table = Table(box=box.MINIMAL)    # Minimal borders
table = Table(box=box.DOUBLE)     # Double-line borders
table = Table(box=None)           # No borders at all

4.3 Progress Bars

Simple progress with track()

The easiest way to add a progress bar:

from rich.progress import track

for item in track(range(100), description="Processing..."):
    # do work
    pass

The Progress class

For more control, use the Progress context manager:

from rich.progress import Progress

with Progress() as progress:
    task = progress.add_task("[green]Downloading...", total=1000)
    while not progress.finished:
        progress.update(task, advance=1)

Multi-task progress

from rich.progress import Progress

with Progress() as progress:
    task1 = progress.add_task("[red]Downloading...", total=1000)
    task2 = progress.add_task("[green]Processing...", total=500)
    task3 = progress.add_task("[cyan]Cooking...", total=200)

    while not progress.finished:
        progress.update(task1, advance=0.9)
        progress.update(task2, advance=0.5)
        progress.update(task3, advance=0.3)

Custom progress columns

from rich.progress import (
    BarColumn,
    DownloadColumn,
    Progress,
    TextColumn,
    TimeRemainingColumn,
    TransferSpeedColumn,
)

progress = Progress(
    TextColumn("[bold blue]{task.fields[filename]}", justify="right"),
    BarColumn(bar_width=None),
    "[progress.percentage]{task.percentage:>3.1f}%",
    DownloadColumn(),
    TransferSpeedColumn(),
    TimeRemainingColumn(),
)

Available built-in columns:

Column Description
TextColumn Arbitrary text with task field interpolation
BarColumn The progress bar itself
SpinnerColumn An animated spinner
TimeElapsedColumn Time since task started
TimeRemainingColumn Estimated time remaining
DownloadColumn Download progress in bytes/KB/MB/GB
TransferSpeedColumn Transfer speed
FileSizeColumn Total file size
MofNCompleteColumn "M of N" style counter
TaskProgressColumn Percentage complete

File copy with progress

import shutil
from rich.progress import Progress

with Progress() as progress:
    desc = "Copying data.bin"
    with progress.open("data.bin", "rb", description=desc) as src:
        with open("data_copy.bin", "wb") as dst:
            shutil.copyfileobj(src, dst)

Note

progress.open() wraps a file object to automatically track read progress. There is also progress.wrap_file() for wrapping arbitrary file-like objects (e.g., HTTP response streams).

Deferred task start

For downloads where you don't know the total size upfront:

task_id = progress.add_task("download", filename="data.zip", start=False)
# ... later, when you know the size:
progress.update(task_id, total=file_size)
progress.start_task(task_id)

4.4 Syntax Highlighting

Rich highlights source code using Pygments, with added features like line numbers and indent guides.

from rich.syntax import Syntax

code = '''
def hello(name: str) -> None:
    """Greet someone."""
    print(f"Hello, {name}!")
'''

syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
console.print(syntax)

Load from file

syntax = Syntax.from_path("example.py", line_numbers=True, indent_guides=True)
console.print(syntax)

Syntax constructor options

Parameter Type Default Description
code str (required) Source code string
lexer str (required) Pygments lexer name ("python", "javascript", etc.)
theme str "monokai" Pygments theme name
line_numbers bool False Display line numbers
start_line int 1 Starting line number
line_range tuple None Only show lines in (start, end) range
highlight_lines set None Set of line numbers to highlight
word_wrap bool False Wrap long lines
indent_guides bool False Show indent guide lines
padding int 0 Padding around code
background_color str None Override background color

CLI utility

# Highlight a file in the terminal
python -m rich.syntax example.py --line-numbers --indent-guides --theme monokai

4.5 Markdown Rendering

Rich renders Markdown directly in the terminal:

from rich.markdown import Markdown

md = Markdown("""
# Heading 1

This is **bold** and *italic* text with `inline code`.

- Item one
- Item two
- Item three

```python
print("Hello from a code block!")

""")

console.print(md)


#### Markdown options

| Parameter | Type | Default | Description |
|---|---|---|---|
| `markup` | `str` | *(required)* | Markdown text |
| `code_theme` | `str` | `"monokai"` | Pygments theme for code blocks |
| `inline_code_lexer` | `str` | `None` | Lexer for inline code |
| `hyperlinks` | `bool` | `True` | Render clickable links (if terminal supports) |
| `justify` | `str` | `None` | Text justification |

#### CLI utility

```bash
# Render a markdown file in the terminal
python -m rich.markdown README.md --force-color --width 80

# With a specific code theme
python -m rich.markdown README.md --code-theme dracula

4.6 Pretty Printing

Rich's pretty printer displays Python data structures with indentation, syntax coloring, and smart wrapping.

Quick install for the REPL

from rich.pretty import install
install()

After calling install(), every expression result in the Python REPL is automatically pretty-printed. This is one of the most popular Rich features for interactive development.

Explicit pretty printing

from rich.pretty import pprint

data = {
    "name": "Rich",
    "features": ["colors", "tables", "progress bars"],
    "nested": {"level1": {"level2": [1, 2, 3]}},
}
pprint(data, indent_guides=True, expand_all=True)

Pretty as a renderable

from rich.pretty import Pretty
from rich.panel import Panel

data = {"key": [1, 2, 3], "other": ("a", "b", "c")}
console.print(Panel(Pretty(data), title="Data"))

Auto-repr with @rich.repr.auto

Automatically generate a Rich-compatible __repr__ for your classes:

import rich.repr

@rich.repr.auto
class Bird:
    def __init__(self, name, eats=None, fly=True, extinct=False):
        self.name = name
        self.eats = list(eats) if eats else []
        self.fly = fly
        self.extinct = extinct

from rich import print
print(Bird("penguin", eats=["fish"], fly=False))

Tip

Use rich.pretty.install() in your Python startup file (PYTHONSTARTUP environment variable) to get Rich pretty printing in every interactive session automatically.

4.7 Tracebacks

Rich produces tracebacks with syntax highlighting, local variable display, and cleaner formatting than Python's default.

from rich.traceback import install
install(show_locals=True)

# Now ALL unhandled exceptions use Rich tracebacks automatically

Manual exception printing

from rich.console import Console

console = Console()

try:
    1 / 0
except Exception:
    console.print_exception(show_locals=True)

install() options

Parameter Type Default Description
show_locals bool False Show local variables in each frame
width int 100 Width of traceback output
extra_lines int 3 Extra lines of code context around the error
theme str None Pygments theme for code
word_wrap bool False Wrap long lines
suppress list [] List of modules/paths to suppress from tracebacks
max_frames int 100 Maximum frames to show (useful for recursion errors)

Suppressing library frames

from rich.traceback import install
import click

install(suppress=[click])
# Click's internal frames will be dimmed in tracebacks

Warning

When using Rich tracebacks with show_locals=True, be aware that local variable values are displayed in the traceback output. Avoid logging tracebacks that may contain sensitive data (passwords, tokens, API keys) to shared locations.

4.8 Trees

Display hierarchical data as an expandable tree:

from rich.tree import Tree

tree = Tree("Root")
branch1 = tree.add("Branch 1")
branch1.add("Leaf 1a")
branch1.add("Leaf 1b")
branch2 = tree.add("Branch 2")
branch2.add("[red]Leaf 2a[/red]")
sub = branch2.add("Sub-branch")
sub.add("Deep leaf")

console.print(tree)

Trees can contain any Rich renderable, including tables, panels, and syntax blocks:

from rich.tree import Tree
from rich.syntax import Syntax

tree = Tree("Project")
src = tree.add("src/")
src.add(Syntax("def main(): pass", "python"))

4.9 Panels and Layout

Panels

Wrap any content in a bordered panel:

from rich.panel import Panel

console.print(Panel("Hello, World!", title="Greeting", border_style="green"))
console.print(Panel.fit("Tight fit panel", border_style="red"))

Panel options include title, subtitle, border_style, padding, expand, and box (border style).

Alignment

from rich.align import Align

console.print(Align.center("Centered text"))
console.print(Align.right("Right-aligned"))
console.print(Align.center("Vertically centered", vertical="middle"))

Columns

Display items in auto-sized columns:

from rich.columns import Columns
from rich.panel import Panel

items = [Panel(f"Item {i}", expand=True) for i in range(12)]
console.print(Columns(items))

Layout (split-pane interfaces)

Build complex split-pane terminal UIs:

from rich.layout import Layout

layout = Layout()
layout.split(
    Layout(name="header", size=3),
    Layout(name="main", ratio=1),
    Layout(name="footer", size=5),
)
layout["main"].split_row(
    Layout(name="sidebar"),
    Layout(name="body", ratio=2, minimum_size=60),
)

layout["header"].update("Header content")
layout["body"].update("Main body")
layout["sidebar"].update("Sidebar")
layout["footer"].update("Footer")

console.print(layout)

For a full-screen application with live updates, combine Layout with Live:

from rich.live import Live

with Live(layout, screen=True, refresh_per_second=10):
    # Update layout regions dynamically
    layout["body"].update("Updated content")

4.10 Live Display

Update terminal output in place without flickering:

from time import sleep
from rich.live import Live
from rich.table import Table

table = Table()
table.add_column("Row")
table.add_column("Value")

with Live(table, refresh_per_second=4):
    for row in range(12):
        sleep(0.4)
        table.add_row(f"{row}", f"[red]value {row}")

Live options:

Parameter Type Default Description
renderable any (required) The renderable to display
console Console None Console instance
screen bool False Use full-screen alternate buffer
refresh_per_second float 4 Maximum refresh rate
transient bool False Clear display when done
redirect_stdout bool True Redirect stdout through Live
redirect_stderr bool True Redirect stderr through Live
vertical_overflow str "ellipsis" How to handle overflow: "crop", "ellipsis", "visible"

4.11 Logging with RichHandler

Replace Python's default logging output with Rich-formatted logs:

import logging
from rich.logging import RichHandler

logging.basicConfig(
    level=logging.DEBUG,
    format="%(message)s",
    datefmt="[%X]",
    handlers=[RichHandler()],
)

log = logging.getLogger("rich")
log.info("Server starting...")
log.warning("Low disk space")
log.error("Connection failed")
log.debug("Received %d bytes", 4096)

RichHandler options:

Parameter Type Default Description
level int NOTSET Logging level
console Console None Console to write to
show_time bool True Show timestamp
show_level bool True Show log level
show_path bool True Show file path and line number
markup bool False Enable Rich markup in log messages
rich_tracebacks bool False Use Rich tracebacks for exceptions
tracebacks_show_locals bool False Show locals in Rich tracebacks
log_time_format str "[%X]" Time format string

Tip

Set markup=True on RichHandler to use Rich markup in your log messages: log.info("[bold green]Success![/]"). Set rich_tracebacks=True to get Rich-formatted exception tracebacks in your logs.

Console.log()

Rich also provides a standalone log method that adds timestamps and source location:

from rich.console import Console

console = Console()
console.log("Starting process...")
console.log("Result:", {"status": "ok", "count": 42}, log_locals=True)

4.12 Prompts

Rich provides styled prompts for user input with built-in validation:

from rich.prompt import Prompt, IntPrompt, FloatPrompt, Confirm

# Basic text prompt
name = Prompt.ask("Enter your name")

# With a default value
name = Prompt.ask("Enter your name", default="World")

# Integer input with validation (re-prompts on invalid input)
age = IntPrompt.ask("Enter your age")

# Float input
weight = FloatPrompt.ask("Enter weight in kg")

# Yes/no confirmation
if Confirm.ask("Continue?"):
    print("Proceeding...")

# Prompt with choices (validates input against choices)
color = Prompt.ask("Pick a color", choices=["red", "green", "blue"])

4.13 Status and Spinners

Status spinner

Show a spinner while a long-running task executes:

from rich.console import Console

console = Console()

with console.status("[bold green]Working on tasks...") as status:
    # do work
    import time
    tasks = [f"task {n}" for n in range(1, 6)]
    while tasks:
        task = tasks.pop(0)
        time.sleep(1)
        console.log(f"{task} complete")

Spinner renderable

Use a spinner as a standalone renderable:

from rich.spinner import Spinner

spinner = Spinner("dots", text="Loading...")
# Use within a Live display or other container

Available spinner styles include: dots, line, pipe, simpleDots, simpleDotsScrolling, star, arc, bouncingBall, pong, and many more. See rich.spinner.SPINNERS for the full list.

4.14 JSON Pretty Printing

From the Python API

from rich import print_json

# From a JSON string
print_json('{"name": "Rich", "version": 13}')

# From a Python object
print_json(data={"users": [{"id": 1, "name": "Alice"}]})

From the Console

from rich.json import JSON

json_display = JSON('{"key": "value", "list": [1, 2, 3]}')
console.print(json_display)

CLI utility

# Pretty print a JSON file
python -m rich.json data.json

# From stdin
cat data.json | python -m rich.json -

# With custom indent
python -m rich.json data.json --indent 4

4.15 Inspect

Examine any Python object interactively:

from rich import inspect

# Basic inspection -- shows attributes and values
inspect([1, 2, 3])

# Show methods
inspect([1, 2, 3], methods=True)

# Show full help text
inspect([1, 2, 3], help=True)

# Show private attributes
inspect([1, 2, 3], private=True)

# Show everything
inspect([1, 2, 3], all=True)

inspect() is particularly useful in the REPL for exploring unfamiliar objects and APIs.

4.16 Exporting Output

Rich can capture and export rendered output to HTML, SVG, or plain text.

from rich.console import Console
from rich.table import Table

# Enable recording
console = Console(record=True)

table = Table(title="Data")
table.add_column("Name")
table.add_column("Value")
table.add_row("Alpha", "100")
table.add_row("Beta", "200")

console.print(table)

# Export methods (return strings)
html = console.export_html()
text = console.export_text()
svg = console.export_svg(title="My Output")

# Save methods (write to files)
console.save_html("output.html", clear=False)
console.save_text("output.txt", clear=False)
console.save_svg("output.svg", title="My Output")

Important

The console output buffer is flushed after each export call by default. Pass clear=False to keep the buffer for subsequent exports. If you need multiple export formats from the same output, use clear=False on all but the last call.

4.17 Themes

Defining custom themes

from rich.console import Console
from rich.theme import Theme

theme = Theme({
    "info": "dim cyan",
    "warning": "magenta",
    "danger": "bold red",
    "success": "bold green",
    "highlight": "italic yellow",
})

console = Console(theme=theme)
console.print("[success]Operation completed successfully[/success]")
console.print("[danger]Critical error occurred[/danger]")

Theme files

Save themes as INI files for reuse:

# styles.ini
[styles]
info = dim cyan
warning = magenta
danger = bold red
success = bold green
from rich.theme import Theme

theme = Theme.read("styles.ini")

4.18 Custom Renderables

Rich objects implement the "Rich protocol" -- any object can be made renderable by implementing __rich__() or __rich_console__():

Simple: __rich__()

Return any Rich renderable:

from rich.panel import Panel
from rich.text import Text

class Header:
    def __init__(self, title):
        self.title = title

    def __rich__(self):
        return Panel(Text(self.title, justify="center"), style="bold blue")

console.print(Header("My Application"))

Advanced: __rich_console__()

Yield Segment objects for complete control:

from rich.console import Console, ConsoleOptions, RenderResult
from rich.segment import Segment
from rich.style import Style

class ColorStripe:
    def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
        for x in range(options.max_width):
            color_index = int(x / options.max_width * 255)
            yield Segment(" ", Style(bgcolor=f"color({color_index})"))
        yield Segment.line()

console.print(ColorStripe())

4.19 Custom Highlighters

Create highlighters that automatically apply styles based on regex patterns:

from rich.console import Console
from rich.highlighter import RegexHighlighter
from rich.theme import Theme

class EmailHighlighter(RegexHighlighter):
    base_style = "email."
    highlights = [r"(?P<address>[\w.-]+@[\w.-]+\.\w+)"]

theme = Theme({"email.address": "bold magenta"})
console = Console(highlighter=EmailHighlighter(), theme=theme)

console.print("Send mail to [email removed] for more info")

The base_style is prepended to each named group in the regex. Define the resulting style names in your theme.


5. Common Tasks

Recipe 1: Styled error messages

from rich.console import Console
from rich.panel import Panel

console = Console(stderr=True)

def error(message: str) -> None:
    console.print(Panel(f"[bold red]{message}", title="Error", border_style="red"))

error("File not found: config.yaml")

Recipe 2: Progress bar for file downloads

from concurrent.futures import ThreadPoolExecutor
from urllib.request import urlopen
from rich.progress import (
    Progress, BarColumn, DownloadColumn, TransferSpeedColumn, TimeRemainingColumn
)

def download(url: str, path: str, progress, task_id):
    response = urlopen(url)
    size = int(response.info()["Content-length"])
    progress.update(task_id, total=size)
    progress.start_task(task_id)
    with open(path, "wb") as f:
        while chunk := response.read(32768):
            f.write(chunk)
            progress.update(task_id, advance=len(chunk))

progress = Progress(
    "[bold blue]{task.fields[filename]}",
    BarColumn(bar_width=None),
    DownloadColumn(),
    TransferSpeedColumn(),
    TimeRemainingColumn(),
)

urls = ["https://example.com/file1.zip", "https://example.com/file2.zip"]

with progress:
    with ThreadPoolExecutor(max_workers=4) as pool:
        for url in urls:
            filename = url.split("/")[-1]
            task_id = progress.add_task("dl", filename=filename, start=False)
            pool.submit(download, url, filename, progress, task_id)

Recipe 3: Directory listing with columns

import os
from rich import print
from rich.columns import Columns
from rich.text import Text

def list_directory(path: str):
    entries = sorted(os.listdir(path), key=str.lower)
    items = []
    for name in entries:
        full = os.path.join(path, name)
        style = "bold blue" if os.path.isdir(full) else "default"
        text = Text(name, style=style)
        text.highlight_regex(r"\.\w+$", "bold")
        items.append(text)
    print(Columns(items, equal=True, column_first=True))

list_directory(".")

Recipe 4: Full-screen dashboard with Live + Layout

from datetime import datetime
from time import sleep
from rich.console import Console
from rich.layout import Layout
from rich.live import Live
from rich.panel import Panel
from rich.table import Table
from rich.text import Text

layout = Layout()
layout.split(
    Layout(name="header", size=3),
    Layout(name="body", ratio=1),
)

def get_header():
    return Panel(Text(datetime.now().ctime(), justify="center"), style="white on blue")

with Live(layout, screen=True, refresh_per_second=4) as live:
    for i in range(60):
        layout["header"].update(get_header())
        layout["body"].update(Panel(f"Tick: {i}"))
        sleep(1)

Recipe 5: Calendar with colored tables

import calendar
from rich import box
from rich.columns import Columns
from rich.console import Console
from rich.table import Table

def print_month(year: int, month: int):
    cal = calendar.Calendar()
    table = Table(
        title=f"{calendar.month_name[month]} {year}",
        box=box.SIMPLE_HEAVY, style="green", padding=0
    )
    for wd in cal.iterweekdays():
        table.add_column(calendar.day_abbr[wd], justify="right")
    for week in cal.monthdayscalendar(year, month):
        table.add_row(*[str(d) if d else "" for d in week])
    return table

console = Console()
months = [print_month(2026, m) for m in range(1, 13)]
console.print(Columns(months, padding=1, expand=True))

Recipe 6: Enhanced logging for a server

import logging
from rich.logging import RichHandler
from rich.highlighter import RegexHighlighter
from rich.theme import Theme

class RequestHighlighter(RegexHighlighter):
    base_style = "req."
    highlights = [
        r"(?P<method>GET|POST|PUT|DELETE)",
        r"(?P<status>[2]\d{2})",
        r"(?P<error>[45]\d{2})",
        r"(?P<path>/\S+)",
    ]

theme = Theme({
    "req.method": "bold cyan",
    "req.status": "bold green",
    "req.error": "bold red",
    "req.path": "magenta",
})

logging.basicConfig(
    level=logging.INFO,
    format="%(message)s",
    handlers=[RichHandler(console=__import__("rich.console", fromlist=["Console"]).Console(theme=theme))],
)

Recipe 7: Exception handling with Rich tracebacks

from rich.traceback import install
install(show_locals=True, suppress=["click", "flask"])

# All unhandled exceptions now show Rich tracebacks
# with local variables visible and framework frames dimmed

Recipe 8: Nested progress bars

from time import sleep
from rich.console import Group
from rich.live import Live
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn

overall = Progress()
jobs = Progress(SpinnerColumn(), TextColumn("{task.description}"), BarColumn())

group = Group(
    Panel(Group(jobs), title="Current Jobs"),
    overall,
)

overall_task = overall.add_task("Total", total=30)

with Live(group):
    for i in range(3):
        job_task = jobs.add_task(f"Job {i+1}", total=10)
        for j in range(10):
            sleep(0.1)
            jobs.advance(job_task)
            overall.advance(overall_task)
        jobs.update(job_task, visible=False)

Recipe 9: Exporting a table as SVG

from rich.console import Console
from rich.table import Table

console = Console(record=True)

table = Table(title="Data Report")
table.add_column("Metric", style="cyan")
table.add_column("Value", justify="right", style="green")
table.add_row("Users", "1,234")
table.add_row("Revenue", "$56,789")

console.print(table, justify="center")
console.save_svg("report.svg", title="Data Report")

Recipe 10: Pretty-printed class instances

import rich.repr

@rich.repr.auto
class Config:
    def __init__(self, host="localhost", port=8080, debug=False, workers=4):
        self.host = host
        self.port = port
        self.debug = debug
        self.workers = workers

from rich import print
print(Config(debug=True, workers=8))
# Output: Config(host='localhost', port=8080, debug=True, workers=8)

Recipe 11: Rendering Markdown files

from rich.console import Console
from rich.markdown import Markdown

console = Console()

with open("README.md") as f:
    md = Markdown(f.read())

console.print(md)

Recipe 12: Interactive object exploration

from rich import inspect

# Discover what methods a string has
inspect("hello", methods=True)

# Explore a module
import json
inspect(json, methods=True, help=True)

6. Troubleshooting

Progress bars and Live display not working in Jupyter

Symptom: Progress bars or Live display produce garbled output or don't update in Jupyter notebooks, particularly with ipykernel>=7.

Cause: Changes in ipykernel 7+ changed how stdout/stderr are handled, breaking Rich's output detection.

Workaround:

from rich.console import Console

# Force Jupyter mode explicitly
console = Console(force_jupyter=True)

If that doesn't help, pin to an older ipykernel:

pip install "ipykernel<7"

Caution

This is a known open issue (#3868). If you encounter it, check the issue for the latest status and workarounds.

Colors not showing in output

Symptom: Output appears as plain text without any colors or styling.

Check the following:

  1. Terminal support: Use a modern terminal (Windows Terminal, iTerm2, GNOME Terminal, etc.). Legacy cmd.exe has limited support.

  2. Piped output: Rich disables color when output is piped to a file or another command. Force it with:

    console = Console(force_terminal=True)
    
  3. NO_COLOR environment variable: If NO_COLOR is set, Rich respects it and disables all color. Unset it if you want color.

  4. TERM=dumb: Rich treats TERM=dumb as a no-color terminal. Check your environment.

Note

Run python -m rich to see a test card. If it renders with colors, Rich and your terminal are working correctly. If it renders without colors, the issue is with your terminal configuration.

Emoji and Unicode characters displaying incorrectly

Symptom: Emoji appear as question marks, boxes, or cause misaligned output.

Solutions:

  • Use a font with good Unicode coverage (e.g., Cascadia Code, JetBrains Mono, Nerd Fonts).

  • Set the UNICODE_VERSION environment variable to match your terminal's Unicode support:

    export UNICODE_VERSION=13.0.0
    
  • Disable emoji rendering:

    console = Console(emoji=False)
    

Rich markup appearing as literal text

Symptom: [bold]text[/bold] prints literally instead of rendering as bold.

Causes:

  • Using Python's built-in print() instead of rich.print() or console.print().

  • Markup disabled on the Console: Console(markup=False).

  • Unescaped square brackets in text. Escape literal brackets:

    console.print("Use \\[bold] for bold")  # Escaped
    

Symptom: When using Rich tracebacks over VSCode Remote SSH, file links in tracebacks point to local filesystem paths instead of the remote server.

Status: Known issue (#4036). The file paths in tracebacks reference the remote server's filesystem, but VSCode may not correctly resolve them as remote paths.

Windows legacy console issues

Symptom: Colors look wrong, or stderr output uses stdout's color handle.

Solutions:

  • Use Windows Terminal instead of legacy cmd.exe or PowerShell ISE.
  • Rich's legacy Windows color layer has a known issue (#4071) where it always uses the stdout handle, even for stderr streams. If you need correct stderr coloring, use Windows Terminal which supports native VT escape sequences.

Warning

The legacy Windows console adapter (rich._win32_console) is a compatibility layer for older Windows terminals. It has known limitations. Always prefer Windows Terminal for the best Rich experience on Windows.

console.print() with no arguments

Symptom: console.print() with no arguments does not behave the same as print().

Explanation: Python's built-in print() outputs a newline when called with no arguments. console.print() also outputs a newline, but if you pass empty *args (e.g., console.print(*[])), the behavior differs. This is a known edge case (#3701).


7. FAQ

Q: Is Rich a CLI framework? A: No. Rich is a library for producing beautiful terminal output. It does not handle argument parsing, command routing, or CLI application structure. For a full CLI framework built on Rich, see Textual (TUI) or use Rich alongside Click or Typer.

Q: What are the python -m rich.* commands? A: Rich includes small convenience utilities for quick terminal use:

  • python -m rich -- displays a feature demo test card
  • python -m rich.json <file> -- pretty-prints a JSON file
  • python -m rich.syntax <file> -- syntax-highlights a source file
  • python -m rich.markdown <file> -- renders a Markdown file

These are convenience wrappers, not the primary interface. Rich is designed to be used as a Python library.

Q: How do I disable all Rich formatting? A: Several approaches:

  • Console(no_color=True) -- disables color but keeps layout
  • Console(force_terminal=False) -- disables terminal features when not a TTY
  • Set NO_COLOR=1 environment variable
  • Set TERM=dumb environment variable

Q: Does Rich work in Jupyter notebooks? A: Yes. Rich auto-detects Jupyter and renders output as HTML in notebook cells. You can force this behavior with Console(force_jupyter=True). Note that ipykernel>=7 introduced a compatibility issue with progress bars (#3868).

Q: How do I use Rich with Python's logging module? A: Use RichHandler:

import logging
from rich.logging import RichHandler
logging.basicConfig(level=logging.INFO, handlers=[RichHandler()])

See Section 4.11 for full details.

Q: Can I export Rich output to HTML or images? A: Yes. Create a Console with record=True, print your content, then use export_html(), export_svg(), or export_text(). See Section 4.16.

Q: How do I make my own objects printable with Rich? A: Implement __rich__() (return a renderable) or __rich_console__() (yield Segments) on your class. See Section 4.18.

Q: What color systems does Rich support? A: Rich supports 4-bit (16 standard colors), 8-bit (256 colors), and 24-bit truecolor (16.7 million colors). It auto-detects the terminal's capability and converts colors down automatically. You can force a specific system with Console(color_system="truecolor").

Q: How do I use Rich alongside other libraries that write to the terminal? A: If another library writes directly to stdout while Rich is using Live or Progress, output can become garbled. Use Live(redirect_stdout=True) (the default) to capture stdout writes, or use console.log() for your own output within a Live context.

Q: What is the performance impact of Rich? A: Rich adds negligible overhead for simple print operations. For complex renderables (large tables, progress bars), Rich caches style calculations internally. The test card (python -m rich) benchmarks cold-cache and warm-cache render times so you can evaluate performance on your system.

For architecture details, rendering internals, and advanced customization, see the Admin Guide.