# How to Use fmtlib for Wide Character Formatting: A Complete Guide to `wchar_t` and `char16_t`

> Master wide character formatting with fmtlib. Learn to use wchar_t and char16_t seamlessly with <fmt/xchar.h> for identical performance to narrow character types.

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

---

**Include `<fmt/xchar.h>` to enable full `wchar_t` support in fmtlib, allowing `fmt::format`, `fmt::print`, and custom formatters to work with wide character strings, literals, and locales with identical performance to narrow character formatting.**

The fmtlib/fmt repository provides a production-ready formatting library for C++, and wide character support is implemented through the [`xchar.h`](https://github.com/fmtlib/fmt/blob/main/xchar.h) header. When you include this file, the library instantiates its generic formatting engine for any non-`char` character type, creating a parallel API that works with `std::wstring`, `std::wostream`, and `wchar_t` literals while sharing the same optimized implementation used for standard `char` formatting.

## Core Architecture of Wide Character Support

### The xchar.h Header Interface

The primary entry point for wide character formatting is [`include/fmt/xchar.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/xchar.h). This header declares the wide-character specific API including `wformat_string`, `wformat_args`, `wmemory_buffer`, and wide overloads of `format`, `print`, `println`, and `vprint`.

According to the source code in lines 88-90 of [`xchar.h`](https://github.com/fmtlib/fmt/blob/main/xchar.h), the `format` function provides an overload `format(wformat_string<T...>, T&&...)` that returns `std::wstring`. The header also defines `make_wformat_args` (lines 27-30) for runtime argument handling and `arg` (lines 41-43) for creating wide character named arguments.

### Generic Template Instantiation

Under the hood, the library reuses the same formatting machinery defined in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h). The `basic_fstring<Char>` and `basic_format_string<Char>` templates are instantiated with `Char = wchar_t` to process wide character strings.

Because the core algorithms in [`include/fmt/format-inl.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format-inl.h) are character-type agnostic, wide character functions obtain the same performance characteristics and features—including positional arguments, precision control, and compile-time format string checking—as their narrow counterparts.

### Locale-Aware Formatting

When `FMT_USE_LOCALE` is defined, the `detail::write_loc` function (lines 46-55 in [`xchar.h`](https://github.com/fmtlib/fmt/blob/main/xchar.h)) enables locale-aware number formatting. This implementation uses `std::numpunct<wchar_t>` to apply thousands separators and decimal points according to the active locale, ensuring that `{:n}` specifiers work correctly for wide output streams.

## Practical Wide Character Formatting Examples

### Basic Formatting with `std::wstring`

To format wide strings, use `L` string literals and include the xchar header. The `fmt::format` function returns a `std::wstring` when provided with wide character arguments.

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

int main() {
    std::wstring msg = fmt::format(L"Hello, {}!", L"world");
    // msg == L"Hello, world!"
}

```

This invokes the `format` overload in [`xchar.h`](https://github.com/fmtlib/fmt/blob/main/xchar.h) that constructs a `wstring` buffer and processes the format string using the same validation engine as narrow characters.

### Printing to Standard Output

Use `fmt::print` with wide string literals to write directly to `stdout` or any `FILE*` stream. The library handles wide character conversion automatically through `vprint` (lines 24-26 in [`xchar.h`](https://github.com/fmtlib/fmt/blob/main/xchar.h)).

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

int main() {
    fmt::print(L"Pi ≈ {:.3f}\n", 3.1415926535);
    // Output: Pi ≈ 3.142
}

```

The `println` variant (lines 33-38) appends `L"\n"` automatically, providing a convenient wrapper around the standard print function.

### Named Arguments with Wide Character Keys

Named arguments work identically for wide characters using `fmt::arg`, which creates a `named_arg<T, wchar_t>` object. The name is stored as a `wchar_t*` and resolved at parse time.

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

int main() {
    std::wstring out = fmt::format(
        L"{name} has {score:.2f} points",
        fmt::arg(L"name", L"Alice"),
        fmt::arg(L"score", 97.5));
    // out == L"Alice has 97.50 points"
}

```

### Locale-Aware Number Grouping

Wide character formatting respects locale settings for number punctuation. The `{:n}` specifier uses `std::numpunct<wchar_t>` via `detail::write_loc` to insert thousands separators.

```cpp
#include <fmt/xchar.h>
#include <locale>

int main() {
    std::locale loc("en_US.UTF-8");
    fmt::print(loc, L"Balance: {:n}\n", 1234567);
    // Output: Balance: 1,234,567
}

```

### Custom Formatters for User-Defined Types

To format custom types with wide characters, specialize `fmt::formatter<T, wchar_t>` instead of `fmt::formatter<T, char>`. The specialization must parse format specifications and write to a wide character output iterator.

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

struct Point {
    int x, y;
};

template <>
struct fmt::formatter<Point, wchar_t> {
    constexpr auto parse(fmt::format_parse_context<wchar_t>& ctx) {
        return ctx.begin();
    }
    
    template <typename FormatContext>
    auto format(const Point& p, FormatContext& ctx) const {
        return fmt::format_to(ctx.out(), L"({},{})", p.x, p.y);
    }
};

int main() {
    Point p{3, 4};
    fmt::println(L"Point: {}", p);   // → Point: (3,4)
}

```

### Runtime Format Strings

When the format string is determined at runtime, use `fmt::make_wformat_args` to build a `wformat_args` object. This is essential for scenarios requiring dynamic format specification while maintaining type safety.

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

int main() {
    const wchar_t* fmt_str = L"Values: {}, {}";
    auto args = fmt::make_wformat_args(10, 20);
    std::wstring result = fmt::format(fmt::runtime(fmt_str), args);
    // result == L"Values: 10, 20"
}

```

The `runtime` function tells the library to skip compile-time checking for that specific format string, while `make_wformat_args` packages the values for the wide character formatting engine.

## Summary

- **Include `<fmt/xchar.h>`** to enable wide character support in the fmtlib/fmt repository, providing access to `wformat_string`, `std::wstring` returning overloads, and wide printing functions.
- **Template instantiation** with `Char = wchar_t` ensures that all features available for narrow strings—precision, width, positional arguments, and named arguments—work identically for wide strings.
- **Locale support** uses `std::numpunct<wchar_t>` via `detail::write_loc` in [`xchar.h`](https://github.com/fmtlib/fmt/blob/main/xchar.h) to enable thousands separators and locale-aware number formatting.
- **Custom formatters** must specialize `fmt::formatter<T, wchar_t>` to handle user-defined types in wide character contexts, reusing `fmt::format_to` with wide string literals.

## Frequently Asked Questions

### What character types does fmtlib support besides `char`?

The [`xchar.h`](https://github.com/fmtlib/fmt/blob/main/xchar.h) header enables formatting for any character type that is not `char`, including `wchar_t`, `char16_t`, and `char32_t`. The library instantiates `basic_fstring<Char>` and related templates for these types, providing full API parity with narrow character formatting.

### Do I need separate format strings for wide character formatting?

Yes, you must use wide string literals (prefixed with `L` for `wchar_t`) to match the character type. The library provides parallel overloads that accept `wformat_string` instead of `format_string`, ensuring type safety by preventing accidental mixing of narrow and wide strings in the same formatting call.

### How do I port a custom formatter from `char` to `wchar_t`?

Specialize `fmt::formatter<T, wchar_t>` instead of `fmt::formatter<T>` (which defaults to `char`). The implementation structure remains identical, but you must use wide string literals in `format_to` calls and accept `format_parse_context<wchar_t>` in your parse method.

### Is wide character formatting slower than narrow character formatting?

No. According to the source code in [`include/fmt/format-inl.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format-inl.h), the core parsing and formatting algorithms are character-type agnostic. Both code paths share the same inline implementations, meaning `wchar_t` formatting achieves identical performance characteristics to `char` formatting when processing equivalent format specifications.