How to Add Colored Terminal Output in C++ Using the fmt Library

The fmt library provides type-safe colored terminal output through ANSI escape sequences defined in <fmt/color.h>, using composable text_style objects created via fg(), bg(), and emphasis factories.

Adding colored output to terminal applications improves readability and user experience without sacrificing type safety. The fmt library (fmtlib/fmt) offers a lightweight solution for generating ANSI-colored text entirely through C++ code, eliminating manual escape sequence management. By including the <fmt/color.h> header, you gain access to a fully composable API that supports both standard 16-color terminal palettes and 24-bit true RGB values.

Core Architecture in include/fmt/color.h

The color functionality is contained entirely within include/fmt/color.h, which defines color representations, style containers, and formatting overloads.

Color Enumerations and RGB Support

The header provides two primary color enumerations. The enum class color (lines 16-63) defines 140+ RGB color constants such as red = 0xFF0000, while enum class terminal_color specifies the standard 8/16 terminal colors for compatibility with limited terminals. For programmatic color generation, the rgb helper struct (lines 90-101) converts hex values or color enum values into separate r, g, and b components used to build true-color escape sequences.

The text_style Class and color_type

Internally, the library packs color data into a 32-bit detail::color_type structure (lines 108-130) that stores foreground or background color information along with bit-field discriminators. The text_style class (lines 135-210) encapsulates foreground color, background color, and emphasis flags (bold, italic, underline) into a single composable object. This implementation performs runtime validation to prevent illegal mixing of terminal_color and RGB color values within the same style.

Composing Styles with Factory Functions

The API exposes three constexpr factory functions (lines 242-270) for constructing styles:

  • fg(detail::color_type) – Creates a style that sets the foreground text color
  • bg(detail::color_type) – Creates a style that sets the background color
  • emphasis enum values – Applies text styling flags (bold, italic, underline) combinable with the pipe operator

These factories enable compile-time composition. For example, fg(fmt::color::red) | fmt::emphasis::bold produces a bold red text style without runtime overhead.

Printing Colored Output

Direct Printing with fmt::print

The fmt::print, fmt::println, and fmt::format functions accept a text_style as their first argument. These functions invoke detail::vformat_to (lines 492-638), which automatically inserts the appropriate ANSI escape sequences (\x1b[...m) before the formatted payload and appends a reset sequence (\x1b[0m) to restore terminal defaults after output.

#include <fmt/color.h>

int main() {
    // Simple foreground color
    fmt::print(fg(fmt::color::magenta), "Magenta text\n");
    
    // Background plus emphasis
    fmt::println(bg(fmt::color::cyan) | fmt::emphasis::bold,
                 "Bold on cyan background");
}

Embedding Styled Values with fmt::styled

The styled helper (lines 560-670) wraps values with a text_style for embedding within format string placeholders. When the formatter encounters a styled argument, it emits the escape codes, forwards the value to the underlying type formatter, and resets the style automatically.

auto styled_num = fmt::styled(42, fg(fmt::color::green) | fmt::emphasis::underline);
fmt::print("Result: {}\n", styled_num);

Complete Working Examples

The following example demonstrates mixing foreground colors, background colors, emphasis, and runtime style composition:

#include <fmt/color.h>
#include <iostream>

int main() {
    // Simple foreground color with type-safe formatting
    fmt::print(fg(fmt::color::red), "Error: {}\n", "Connection failed");
    
    // Combining background, foreground, and emphasis
    fmt::print(bg(fmt::color::yellow) | fg(fmt::color::black) | fmt::emphasis::bold,
               "Warning: Low memory\n");
    
    // True-color RGB values using the rgb helper
    fmt::print(fg(fmt::rgb(0xFFA500)), "Custom orange text\n");
    
    // Runtime style composition
    fmt::text_style style = fmt::emphasis::italic;
    style = style | fg(fmt::color::cyan);
    fmt::print(style, "Dynamic styling\n");
    
    // Embedding styled values in format strings
    fmt::print("Value: {}\n",
               fmt::styled(3.14159, fg(fmt::color::yellow) | fmt::emphasis::italic));
}

The library automatically selects the optimal escape sequence format: it emits classic \x1b[31m style codes for terminal_color values and true-color sequences \x1b[38;2;r;g;bm for RGB color values.

Summary

  • Include <fmt/color.h> to access the text_style API and color definitions
  • Use fg(), bg(), and emphasis factories to create composable style objects
  • Combine styles with the operator| pipe operator for complex formatting
  • Pass styles directly to fmt::print() functions or wrap values with fmt::styled() for embedding
  • Automatic reset ensures all formatted output appends \x1b[0m to restore terminal defaults
  • Type safety prevents mixing terminal colors with RGB colors in the same style object

Frequently Asked Questions

Which header file provides colored output in fmt?

The <fmt/color.h> header defines all color functionality including the text_style class, color and terminal_color enumerations, the rgb struct, and the fg(), bg(), and styled() helper functions.

Can I mix terminal colors and RGB colors in the same text_style?

No. The text_style implementation in include/fmt/color.h performs runtime checks to prevent mixing terminal_color values with RGB color values within the same style object, ensuring consistent ANSI escape sequence generation and preventing undefined behavior.

How do I reset colors after printing?

You don't need to manually reset colors. The formatting functions automatically append the reset escape sequence \x1b[0m after the formatted output, as implemented in the detail::vformat_to logic (lines 492-638), ensuring subsequent text uses terminal defaults.

Is the color API compatible with custom format specifications?

Yes. The text_style overloads work with all fmt formatting functions including fmt::format, fmt::print, and fmt::println, supporting the same argument substitution, width, precision, and alignment specifications as standard uncolored formatting.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →