# fmtlib Format String Syntax: The Complete Guide to Modern C++ Formatting

> Unlock fmtlib's powerful format string syntax. Learn to use curly-brace mini-language with replacement fields for advanced C++ value formatting including width, precision, and alignment.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: deep-dive
- Published: 2026-09-08

---

**fmtlib uses a Python-style curly-brace mini-language where replacement fields `{arg_id:format_spec}` control how values are rendered, supporting positional arguments, named arguments, width, precision, alignment, and type-specific formatting.**

The `{fmt}` library—commonly known as **fmtlib**—provides a fast, type-safe alternative to `printf` and `iostreams` for C++. Its **format string syntax** combines the readability of Python's `str.format()` with compile-time validation and extensibility. Every format string in `fmt::format`, `fmt::print`, and related functions follows a well-defined grammar implemented across the `include/fmt/` headers.

---

## Anatomy of a Replacement Field

A **replacement field** is the core unit of fmtlib's format string syntax. Fields are delimited by curly braces and follow this grammar:

```

{ [arg_id] [ : (format_spec | chrono_format_spec) ] }

```

Text outside replacement fields is copied verbatim. To output literal braces, double them: `{{` produces `{` and `}}` produces `}`.

### Argument Selection with arg_id

The **arg_id** component selects which argument to format:

| Style | Example | Behavior |
|-------|---------|----------|
| **Automatic** | `"{}"` | Consumes arguments left-to-right |
| **Positional** | `"{1}"` | Selects argument at index 1 (0-based) |
| **Named** | `"{name}"` | Matches `fmt::arg("name", value)` |

Mixing automatic and explicit positional indices is prohibited—the parser enforces this at compile time when possible.

---

## Format Specification Syntax

The **format_spec** follows the colon in a replacement field and controls presentation details. The full grammar is documented at [[`doc/syntax.md`](https://github.com/fmtlib/fmt/blob/main/doc/syntax.md)](https://github.com/fmtlib/fmt/blob/main/doc/syntax.md#format-spec).

### Fill and Alignment

Controls padding character and placement:

```cpp
fmt::format("[{:*^10}]", "42");   // => "[****42****]"
fmt::format("[{:<10}]", "left");  // => "[left      ]"
fmt::format("[{:>10}]", "right"); // => "[     right]"

```

| Specifier | Meaning |
|-----------|---------|
| `<` | Left-align |
| `>` | Right-align |
| `^` | Center-align |
| Any character before `<>^` | Fill character (default: space) |

### Sign, Alternate Form, and Zero Padding

```cpp
fmt::format("{:+d}", 42);       // => "+42"      (always show sign)
fmt::format("{: d}", 42);       // => " 42"      (space for positive)
fmt::format("{:08d}", 42);      // => "00000042" (zero-padding)
fmt::format("{:#x}", 255);      // => "0xff"     (alternate form)
fmt::format("{:+#010x}", 255);  // => "+0x0000ff" (combined)

```

| Flag | Effect |
|------|--------|
| `+` | Always show sign for signed numbers |
| `-` | Show minus only (default) |
| ` ` (space) | Leading space for positive numbers |
| `#` | Alternate form (prefix for base, decimal point for floats) |
| `0` | Zero-pad to width (ignores fill/align) |

### Width and Precision

Both can be **static values** or **dynamic values** via nested replacement fields:

```cpp
// Static width
fmt::format("[{:10}]", "hi");           // => "[hi        ]"

// Dynamic width from argument
fmt::format("[{:{} }]", "hi", 10);      // => "[hi        ]"

// Static precision
fmt::format("{:.2f}", 3.14159);         // => "3.14"

// Dynamic precision
fmt::format("{:.{}f}", 3.14159, 1);     // => "3.1"

// Width and precision from arguments
fmt::format("{:{}.{}f}", 3.14159, 8, 2); // => "[    3.14]"

```

### Locale-Aware Formatting with `L`

The `L` flag enables locale-specific formatting such as thousands separators:

```cpp
auto loc = std::locale("en_US.UTF-8");
fmt::format(loc, "{:L}", 1234567);      // => "1,234,567"
fmt::format(loc, "{:L}", 1234.5);       // => "1,234.5"

```

### Type Specifiers

The final character (or characters) in format_spec determines the presentation type:

| Type | Applies To | Output Style |
|------|-----------|--------------|
| `d`, `i` | Integer | Decimal |
| `b` | Integer | Binary (with `#` → `0b` prefix) |
| `B` | Integer | Binary uppercase (`0B`) |
| `o` | Integer | Octal |
| `x` | Integer | Hexadecimal |
| `X` | Integer | Hexadecimal uppercase |
| `f`, `F` | Floating | Fixed-point (`F` for uppercase INF/NAN) |
| `e`, `E` | Floating | Scientific notation |
| `g`, `G` | Floating | General (shorter of `f`/`e`) |
| `a`, `A` | Floating | Hexadecimal float |
| `s` | String/string-like | Plain string |
| `?` | String | Debug/quoted string |
| `p` | Pointer | `0x` prefix address |
| `c` | Integer/char | Character |
| `?` | Any | Debug representation |

```cpp
fmt::format("{:b}", 255);      // => "11111111"
fmt::format("{:#b}", 255);     // => "0b11111111"
fmt::format("{:X}", 255);      // => "FF"
fmt::format("{:e}", 1234.5);   // => "1.234500e+03"
fmt::format("{:p}", nullptr);  // => "0x0"

```

---

## Chrono Format Specification

For `std::chrono` durations, time points, and `std::tm`, fmtlib extends the basic spec with **chrono_format_spec**. This uses standard `strftime`-style conversion specifiers:

```cpp
auto t = std::tm{};
t.tm_year = 2023 - 1900;
t.tm_mon = 3;      // April (0-based)
t.tm_mday = 5;

fmt::format("{:%Y-%m-%d %H:%M:%S}", t);  // => "2023-04-05 00:00:00"
fmt::format("{:%B %d, %Y}", t);          // => "April 05, 2023"

// With chrono durations
using namespace std::chrono;
fmt::format("{:%H:%M:%S}", 3661s);       // => "01:01:01"

```

The chrono grammar supports width, precision, and locale modifiers alongside time conversion characters. See [`doc/syntax.md#chrono-format-spec`](https://github.com/fmtlib/fmt/blob/main/doc/syntax.md#chrono-format-spec) for the complete reference.

---

## Practical Code Examples

### Basic Positional and Named Arguments

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

// Automatic indexing
fmt::print("Hello, {}!\n", "world");

// Explicit positional (0-based, reorderable)
fmt::print("{1} {0} {2}\n", "a", "b", "c");  // => "b a c"

// Named arguments with fmt::arg
fmt::print("{greeting}, {name}!\n",
           fmt::arg("greeting", "Good morning"),
           fmt::arg("name", "Developer"));

```

### Advanced Format Specifications

```cpp
#include <fmt/format.h>
#include <fmt/chrono.h>
#include <iostream>
#include <chrono>

// Table-like formatting with alignment
fmt::print("{:<10} {:>6} {:>8}\n", "Item", "Qty", "Price");
fmt::print("{:<10} {:>6} {:>8.2f}\n", "Apples", 12, 3.5);
fmt::print("{:<10} {:>6} {:>8.2f}\n", "Oranges", 6, 2.25);

// Binary/hex debugging output
for (uint8_t b : {0x41, 0x42, 0x43}) {
    fmt::print("0b{:08b} 0x{:02X} '{}'\n", b, b, b);
}

// Chrono with custom formatting
auto now = std::chrono::system_clock::now();
fmt::print("ISO: {:%Y-%m-%dT%H:%M:%S%z}\n", now);

```

### Compile-Time Validation

When format strings are string literals, errors are caught at compile time:

```cpp
// This compiles: type matched
fmt::format("Value: {}", 42);

// This fails at compile time: type mismatch
// fmt::format("Value: {:f}", "not a float");  // ERROR

// This fails at compile time: index out of range
// fmt::format("{2}", "a", "b");  // ERROR: argument index out of range

```

Runtime format strings (via `fmt::runtime`) defer validation:

```cpp
std::string user_fmt = "{:.{}f}";
fmt::format(fmt::runtime(user_fmt), 3.14, 1);  // Runtime parsing

```

---

## Implementation and Source Files

The format string syntax is parsed and interpreted across these key source locations:

- **[`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)** — Declares `fmt::format`, `fmt::print`, and the core `format_string` type that drives compile-time validation
- **[`include/fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/chrono.h)** — Implements chrono_format_spec parsing for date/time types
- **[`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h)** — Provides `fmt::printf` for legacy printf-style format strings
- **[`doc/syntax.md`](https://github.com/fmtlib/fmt/blob/main/doc/syntax.md)** — Definitive grammar documentation for all specification components

The compile-time parsing leverages C++20 `consteval` (or `constexpr` techniques in C++17) to validate format strings against argument types before program execution.

---

## Summary

- **Structure**: fmtlib format strings contain literal text and replacement fields `{arg_id:format_spec}`, with doubled braces for literal braces
- **Arguments**: Support automatic positional, explicit positional, and named arguments via `fmt::arg`
- **Specifications**: After the colon, control fill/align, sign, `#` alternate form, `0` padding, width, precision, `L` locale, and presentation type
- **Chrono**: Special format spec for time types using `%`-based conversion characters
- **Safety**: String literal format strings are validated at compile time; runtime strings use `fmt::runtime`

---

## Frequently Asked Questions

### What is the difference between `fmt::format` and `std::format`?

`fmt::format` is the original implementation in fmtlib that became the basis for C++20's `std::format`. The fmtlib version offers broader compiler support (C++11 and later), additional features like dynamic width/ precision with runtime values, and faster release cycles. As of C++20, `std::format` provides the same core syntax with standard library integration.

### How do I escape curly braces in fmtlib format strings?

Double the braces: `{{` produces a literal `{` and `}}` produces a literal `}`. This is necessary when you need brace characters in output, such as generating JSON or C++ code templates.

### Can I mix automatic and manual argument indexing?

No—fmtlib prohibits mixing `{}` (automatic) with `{0}`, `{1}` (explicit positional) in the same format string. Choose one style per call. Named arguments can coexist with automatic indexing but not with explicit positional indices.

### What happens if my format string has an error?

For string literal format strings, errors trigger **compile-time failures** with descriptive messages. For runtime format strings passed via `fmt::runtime`, errors throw `fmt::format_error` at execution time.