# How to Create Custom Formatters in fmtlib: A Complete Guide

> Learn how to create custom formatters in fmtlib by specializing fmt::formatter or inheriting from existing formatters. This complete guide shows you how to implement parse and format functions for your types.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: how-to-guide
- Published: 2026-09-06

---

**You create custom formatters in fmtlib by specializing the class template `fmt::formatter<T>` for your type `T` and implementing the `parse` and `format` member functions, or by inheriting from an existing formatter such as `fmt::formatter<std::string_view>` to reuse standard specifier parsing.**

The fmtlib/fmt repository provides a fast, extensible C++ formatting library that formats values by discovering specializations of `fmt::formatter<T>`. To add support for user-defined types, you provide a template specialization that tells the library how to parse format specifiers and how to write the formatted representation to a `format_context`. The core definition of the formatter interface resides in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), while detailed examples and customization patterns are documented in [`doc/api.md`](https://github.com/fmtlib/fmt/blob/main/doc/api.md).

## Understanding the fmtlib Formatter Architecture

According to the source code in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), fmtlib formats any value by looking for a specialization of the class template `fmt::formatter<T>`. A valid formatter must provide two key members:

- **`parse`** – A `constexpr` function that consumes characters from a `format_parse_context` to interpret fill, alignment, width, and custom specifiers.
- **`format`** – A function that receives the value and a `format_context`, writes the output, and returns an iterator pointing past the last character written.

The library ships with a generic `formatter` base that handles common specifiers, allowing you to inherit existing functionality rather than implementing everything from scratch.

## Specializing fmt::formatter for User-Defined Types

### Formatting Simple Enums by Inheriting Existing Formatters

For enums and simple types that map cleanly to strings, inherit from `fmt::formatter<std::string_view>` to automatically support standard string specifiers like alignment and width.

```cpp
// color.h
#include <fmt/base.h>

enum class color { red, green, blue };

template <> struct fmt::formatter<color> : fmt::formatter<std::string_view> {
  // parse is inherited from formatter<string_view>
  auto format(color c, fmt::format_context& ctx) const -> fmt::format_context::iterator {
    std::string_view name = "unknown";
    switch (c) {
      case color::red:   name = "red";   break;
      case color::green: name = "green"; break;
      case color::blue:  name = "blue";  break;
    }
    return fmt::formatter<std::string_view>::format(name, ctx);
  }
};

```

```cpp
// main.cc
#include "color.h"
#include <fmt/format.h>

int main() {
    fmt::print("{:>10}\n", color::blue);   // → "      blue"
}

```

Because the specialization inherits from `formatter<std::string_view>`, standard fill and alignment specifiers work automatically. The `format` method maps the enum to a readable string and delegates the actual writing to the base class implementation.

### Formatting Composite Types with nested_formatter

For classes containing multiple fields that should share the same formatting specifiers, use `fmt::nested_formatter<T>` as defined in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h). This base class provides a full `parse` implementation and a `nested` helper that applies the same width and precision to each sub-object.

```cpp
// point.h
#include <fmt/format.h>

struct point {
    double x, y;
};

template <> struct fmt::formatter<point> : fmt::nested_formatter<double> {
    auto format(point p, fmt::format_context& ctx) const -> fmt::format_context::iterator {
        return fmt::write(ctx, "(", nested(p.x), ", ", nested(p.y), ")");
    }
};

```

```cpp
// main.cc
#include "point.h"
#include <fmt/print.h>

int main() {
    fmt::print("[{:>20.2f}]\n", point{1.0, 2.0});
    // → [          (1.00, 2.00)]
}

```

The `nested_formatter<double>` base supplies the logic to parse width and precision specifiers. Inside `format`, the `nested` helper applies these rules to each `double` member, ensuring consistent formatting across the composite object.

### Enabling Formatters for Type Hierarchies with SFINAE

To create a single formatter that handles all types derived from a common base, use a partial specialization with `std::enable_if_t`. This pattern, demonstrated in [`doc/api.md`](https://github.com/fmtlib/fmt/blob/main/doc/api.md), leverages SFINAE to restrict the template to subclasses of your base type.

```cpp
// demo.h
#include <fmt/format.h>
#include <type_traits>

struct A {
    virtual ~A() = default;
    virtual std::string name() const { return "A"; }
};

struct B : A {
    std::string name() const override { return "B"; }
};

template <typename T>
struct fmt::formatter<T,
    std::enable_if_t<std::is_base_of_v<A, T>, char>>
    : fmt::formatter<std::string> {
    auto format(const A& a, fmt::format_context& ctx) const -> fmt::format_context::iterator {
        return fmt::formatter<std::string>::format(a.name(), ctx);
    }
};

```

```cpp
// demo.cc
#include "demo.h"
#include <fmt/print.h>

int main() {
    B b;
    A& a = b;
    fmt::print("{}\n", a);   // → "B"
}

```

The `enable_if_t` condition ensures this specialization only participates in overload resolution for types derived from `A`, allowing polymorphic formatting while reusing the `std::string` formatter implementation.

## Implementing parse and format Methods

When inheriting from `fmt::formatter<std::string_view>` or `fmt::nested_formatter<T>`, you typically reuse the base `parse` method. However, for custom syntax you must implement your own `constexpr` `parse` function that iterates over the `format_parse_context` and returns an iterator to the closing `}`.

The `format` method must accept a `const` reference to your type and a `format_context`, then return an iterator (usually via `fmt::format_to` or by calling another formatter's `format` method). You can locate the return iterator type via `fmt::format_context::iterator`.

## Summary

- **Specialize `fmt::formatter<T>`** in the `fmt` namespace to teach the library how to format your type.
- **Inherit from existing formatters** like `fmt::formatter<std::string_view>` or `fmt::nested_formatter<U>` to reuse specifier parsing logic.
- **Implement `format`** to map your type to output, delegating to base formatters or using `fmt::write`/`fmt::format_to`.
- **Use SFINAE** with `std::enable_if_t` to create generic formatters for entire type hierarchies.
- **Reference source files** [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) for the interface and [`doc/api.md`](https://github.com/fmtlib/fmt/blob/main/doc/api.md) for official examples and best practices.

## Frequently Asked Questions

### How do I create a custom formatter for a simple enum in fmtlib?

Specialize `fmt::formatter<YourEnum>` and inherit from `fmt::formatter<std::string_view>`. Implement only the `format` method to map enum values to strings, then delegate to the base formatter's `format` method to handle the actual output and specifiers.

### What is the difference between parse and format in fmt::formatter?

The `parse` method is a `constexpr` function that reads format specifiers (like width and alignment) from a `format_parse_context` during compile time or runtime setup. The `format` method receives the actual value and a `format_context`, writes the final string representation, and returns an iterator past the output.

### Can I use SFINAE to create a generic formatter for a class hierarchy?

Yes. Define a partial specialization of `fmt::formatter<T, std::enable_if_t<std::is_base_of_v<Base, T>, char>>` to match all types derived from `Base`. This technique, documented in [`doc/api.md`](https://github.com/fmtlib/fmt/blob/main/doc/api.md), lets you write one formatter that handles any subclass through polymorphic references.

### Where can I find examples of custom formatters in the fmtlib source code?

The official examples reside in [`doc/api.md`](https://github.com/fmtlib/fmt/blob/main/doc/api.md), while the core interface is defined in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h). Production-ready specializations for standard library types are located in [`include/fmt/std.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/std.h), providing reference implementations for types like `std::optional` and `std::filesystem::path`.