Rich Help Formatting Overview
Typer integrates with the Rich library to provide colorful, well-structured, and highly readable CLI help outputs. This integration is primarily implemented in typer/rich_utils.py and is exposed through the Typer class and various parameter models.
Core Implementation: rich_utils
The typer/rich_utils.py module serves as the engine for generating Rich-formatted help. It replaces the standard Click help formatting with a system that uses Rich's Console, Panel, Table, and Markdown components.
The main entry point is rich_format_help(), which coordinates the rendering of:
- Usage: Printed with a specific
STYLE_USAGE_COMMAND. - Help Text: Extracted from docstrings and rendered via
_get_help_text(). - Arguments and Options: Organized into panels via
_print_options_panel(). - Subcommands: Organized into panels via
_print_commands_panel().
Highlighting and Styling
Typer uses custom RegexHighlighter classes to apply styles to CLI syntax automatically:
OptionHighlighter: Highlights switches (e.g.,-f), options (e.g.,--formal), and metavars (e.g.,<name>).MetavarHighlighter: Dims separators like[or|in metavars.
Default styles are defined at the top of typer/rich_utils.py, such as STYLE_OPTION = "bold cyan" and STYLE_SWITCH = "bold green".
Markup Modes
Typer supports different ways to parse and render help text through the rich_markup_mode parameter. This can be set when initializing a Typer app:
import typer
app = typer.Typer(rich_markup_mode="markdown")
The available modes are:
"rich"(Default): Uses Rich Console Markup. You can use tags like[bold red]Alert![/]in your docstrings or help parameters."markdown": Parses the text as Markdown. This is particularly useful for complex docstrings with lists or code blocks.None: Disables Rich formatting and falls back to standard Click help.
The _make_rich_text() function in typer/rich_utils.py handles the conversion based on the selected mode:
def _make_rich_text(
*, text: str, style: str = "", markup_mode: MarkupModeStrict
) -> Markdown | Text:
text = inspect.cleandoc(text)
if markup_mode == MARKUP_MODE_MARKDOWN:
text = Emoji.replace(text)
return Markdown(text, style=style)
else:
# ... handles Rich markup or ANSI characters
return highlighter(Text.from_markup(text, style=style))
The Panel System
One of the most powerful features of Typer's Rich integration is the ability to group CLI elements into named panels using the rich_help_panel parameter. This is supported for typer.Option, typer.Argument, and subcommands.
Grouping Options and Arguments
By default, Typer groups parameters into "Arguments" and "Options" panels. You can override this to create custom logical groups:
@app.command()
def main(
name: str = typer.Argument(..., rich_help_panel="User Info"),
email: str = typer.Option(None, rich_help_panel="User Info"),
config: Path = typer.Option(None, rich_help_panel="System"),
):
...
In rich_utils.py, the rich_format_help() function iterates through parameters and groups them by their rich_help_panel attribute before calling _print_options_panel().
Grouping Subcommands
Similarly, subcommands in a Typer group can be organized into panels:
sub_app = typer.Typer(rich_help_panel="Management Commands")
app.add_typer(sub_app, name="users")
Advanced Formatting and Control
Manual Wrapping with \b
Typer uses inspect.cleandoc() to normalize indentation in help strings. By default, it also replaces single newlines with spaces to allow for automatic terminal wrapping. To prevent this and preserve manual line breaks, start a paragraph with the \b escape character:
@app.command()
def main():
"""
Main command.
\b
This paragraph will
preserve its
line breaks.
"""
...
The logic in _get_help_text() specifically checks for this:
if markup_mode != MARKUP_MODE_MARKDOWN and not first_line.startswith("\b"):
first_line = first_line.replace("\n", " ")
Environment Variables
The Rich integration can be controlled via environment variables:
TYPER_USE_RICH: Set to0,false, orFalseto disable Rich formatting globally.TERMINAL_WIDTH: Overrides the detected terminal width for help output._TYPER_FORCE_DISABLE_TERMINAL: Forces Typer to treat the output as a non-terminal, disabling colors and styles.
Integration with Click
Typer's TyperCommand and TyperGroup classes (found in typer/core.py) override the standard Click format_help method. If Rich is enabled and a markup mode is set, they delegate the work to rich_utils.rich_format_help():
def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
if not HAS_RICH or self.rich_markup_mode is None:
return super().format_help(ctx, formatter)
from . import rich_utils
return rich_utils.rich_format_help(
obj=self,
ctx=ctx,
markup_mode=self.rich_markup_mode,
)