Skip to content

Text Markup & Formatting

Markup

rich-click supports 4 different values for text_markup, which determines how text is rendered:

  • 'ansi': (Default) Rendered as plain text with ANSI escape codes handled.
  • 'rich': Rendered using Rich's markup syntax.
  • 'markdown': Rendered with markdown.
  • None: Rendered as plain text, ANSI escape codes are not handled.
rich-click ≥1.8.0 deprecation

Prior to rich-click 1.8.0, markup was controlled by the booleans use_rich_markup and use_markdown.

These booleans have been silently deprecated (read: they will still be supported for the distant future), and users are encouraged to use the text_markup config option instead.

Rich markup

In order to be as widely compatible as possible with a simple import, rich-click does not parse rich formatting markup (eg. [red]) by default. You need to opt-in to this behaviour.

Remember that you'll need to escape any regular square brackets using a back slash in your help texts, for example: [dim]\[my-default: foo][\]

For more information, read the Rich docs on markup and styles.

help_config = {"text_markup": "rich"}
help_config = click.RichHelpConfiguration(text_markup="rich")
click.rich_click.TEXT_MARKUP = "rich"

python examples/04_rich_markup.py --help

See examples/04_rich_markup.py for an example.

Markdown

If you prefer, you can use Markdown text.

help_config = {"text_markup": "markdown"}
help_config = click.RichHelpConfiguration(text_markup="markdown")
click.rich_click.TEXT_MARKUP = "markdown"

python examples/05_markdown.py --help

See examples/05_markdown.py for an example.

Markup in text

The selected text_markup is used to render all text in your CLI help that can be set in the high-level API:

  • The command's help
  • Each subcommand's help
  • Each parameter's help
  • Each panel's help
  • Header text, footer text, and epilog text.
# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click

@click.command(epilog="[blue]For more information, read the docs[/blue]")
@click.option("--foo", "-f", help="[green]foo[/]")
@click.option("--bar", "-b", help="[default on green]bar[/]")
@click.option_panel("Options",
                    help="[magenta not dim]These are [bold]all[/b] the options available.[/]")
@click.rich_config({"text_markup": "rich"})
def cli():
    """CLI for my [red]app[/red].

    This is a demonstration of
    [#FF6B6B bold]r[/][#FF8E53 bold]i[/][#FFB347 bold]c[/][#4ECDC4 bold]h[/]
    markup in CLI help text.
    """

if __name__ == "__main__":
    cli()
Output

python rich_markup.py --help

You can also set the styles of help for objects in decorators using help_style=. The below code renders the same as the above code:

# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click

@click.command(epilog="[blue]For more information, read the docs[/blue]")
@click.option("--foo", "-f", help="foo", help_style="green")
@click.option("--bar", "-b", help="bar", help_style="default on green")
@click.option_panel("Options",
                    help="These are [bold]all[/b] the options available.",
                    help_style="magenta not dim")
@click.rich_config({"text_markup": "rich"})
def cli():
    """CLI for my [red]app[/red].

    This is a demonstration of
    [#FF6B6B bold]r[/][#FF8E53 bold]i[/][#FFB347 bold]c[/][#4ECDC4 bold]h[/]
    markup in CLI help text.
    """

if __name__ == "__main__":
    cli()
Output

python markup_with_help_style.py --help

Emojis

Emoji codes are rendered by default in 'rich' and 'markdown' text markup modes. For other modes, including the default mode, emojis are not rendered.

You can explicitly enable and disable emojis with the config option text_emoji:

# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click

@click.command()
@click.option("--foo", "-f", help="Boo! :ghost: Oops, I mean... foo! :sweat_smile:")
@click.option("--bar", "-b", help="Bar - :bar_chart:")
@click.rich_config({"text_emojis": True})
def cli():
    """Demo of :heart_eyes_cat: emojis!

    Note that this text is not rich markup: [b]See?[/b] :joy:

    Emojis can be set independently of markup. :point_up: :nerd_face:
    """

if __name__ == "__main__":
    cli()
Output

python emojis.py --help

You can view all available emojis with the following command:

uvx --from rich -- python3 -m rich.emoji

Newlines

Handling newlines on rich-click's end involves discretion because in a docstring, newlines can represent a softwrap; they are not necessarily genuine new lines.

For 'markdown' mode, we send all newline handling directly to rich.markdown.Markdown()

For other modes, we implement the following rules:

  • Double newlines collapse to single newlines by default.
  • Single newlines are not preserved, unless...
  • The line starts with '- ', '* ', '> ' or '    ' (4 spaces).

The following code snippet demonstrates this:

# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click

@click.command("newline-control")
def cli():
    """Newline
    control

    Double newlines are always preserved.
    But single newlines are usually not.

    There are however a few situations where we preserve single newlines.

    Indented lines with 4+ spaces are preserved:
        ```python
        from foo import bar
        \n\
        bar.action()
        ```

    Unordered lists are preserved:

    - like
    - this

    also:

    * newlines within unordered lists
      are collapsed
      down.
    * pretty neat!

    Last but not least, we preserve:

    > block
    > quotes
    """

if __name__ == "__main__":
    cli()
Output

python newline_control.py --help

Note that this differs from how base Click handles newlines. The following is the same CLI help text but using import click instead of import rich_click as click:

Output - import click instead of import rich_click as click

python newline_control_base_click.py --help

Spacing

By default, rich-click renders double newlines as single newlines. This is a deliberate decision to make help text take up less vertical space.

However, you can override this behavior by setting the config option text_paragraph_linebreaks to "\n\n".

# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click

@click.command("newline-control")
@click.rich_config({"text_paragraph_linebreaks": "\n\n"})
def cli():
    """Newline
    control

    Double newlines are always preserved.
    But single newlines are usually not.

    There are however a few situations where we preserve single newlines.

    Indented lines with 4+ spaces are preserved:
        ```python
        from foo import bar
        \n\
        bar.action()
        ```

    Unordered lists are preserved:

    - like
    - this

    also:

    * newlines within unordered lists
      are collapsed
      down.
    * pretty neat!

    Last but not least, we preserve:

    > block
    > quotes
    """

if __name__ == "__main__":
    cli()
Output

python newline_control_double.py --help

Info

This default behavior of collapsing newlines is not true when text_markup='markdown'. For markdown, we do not do any newline manipulation.