# How to Use fmtlib with Different Character Encodings: UTF-8, UTF-16, and Wide Strings

> Learn to use fmtlib with UTF-8, UTF-16, and wide strings. Discover automatic encoding selection for effortless formatting with fmtlib, no manual conversions needed.

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

---

**The fmt library automatically selects the correct encoding based on your format string's character type, requiring no explicit conversion calls for UTF-8 `char`, UTF-16 `char16_t`, UTF-32 `char32_t`, or `wchar_t` strings.**

The {fmt} library (fmtlib/fmt) provides fully-templated formatting functions that handle multiple character encodings through a single unified API. Whether you are working with standard UTF-8 `char` strings, UTF-16 `char16_t` data, or platform-specific wide characters, fmtlib deduces the appropriate encoding automatically from your format string. This design eliminates the need for manual conversion utilities while maintaining type safety across different string representations.

## Understanding Character Type Deduction in fmtlib

The core implementation in [`include/fmt/base.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/base.h) defines an abstract *character type* (`Char`) that parameterizes all formatting operations. When you invoke `fmt::format`, the compiler deduces the `Char` template argument from your format string literal—`char` for standard strings, `char16_t` for UTF-16, or `char32_t` for UTF-32.

The `basic_string_view<Char>` type and argument handling utilities in [`include/fmt/args.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/args.h) support this polymorphic behavior. This architecture ensures that [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) can instantiate the same formatting logic for any character width without code duplication.

## UTF-8 Encoding (Default Behavior)

For standard UTF-8 text, fmtlib uses `char` as the default character type. The primary overloads such as `fmt::format` and `fmt::print` operate on `std::string` and `std::string_view` without requiring additional template parameters.

In [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), the template specialization for `Char = char` represents the library's default code path. The implementation treats `char` strings as UTF-8 on most platforms, ensuring compatibility with standard string literals and the C++ standard library.

## UTF-16 and UTF-32 Encoding Support

### UTF-16 (`char16_t`) Implementation

When you pass a UTF-16 string literal (prefixed with `u`) or a `std::u16string_view`, fmtlib instantiates the formatting machinery with `Char = char16_t`. The resulting output is a `std::u16string`.

According to the source code in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) at line 1502, the library implements an internal converter to handle transitions between UTF-8 literals and UTF-16 output. This automatic conversion path allows you to mix UTF-8 arguments with UTF-16 format strings seamlessly.

### UTF-32 (`char32_t`) Implementation

Analogous to UTF-16, using a UTF-32 string literal (prefixed with `U`) or `std::u32string_view` triggers the `char32_t` specialization. The formatted result returns as `std::u32string`, maintaining full 32-bit code point precision throughout the formatting operation.

## Wide Character Support (`wchar_t`)

Platform-specific wide character support resides in [`include/fmt/xchar.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/xchar.h). On Windows, `wchar_t` represents UTF-16 code units, while on POSIX systems it typically represents UTF-32. The header provides overloads such as `fmt::format(wformat_string, ...)` that work with `std::wstring`.

At line 64 of [`include/fmt/xchar.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/xchar.h), the `vformat_to` overload explicitly static-asserts that `Char` is not `char`, ensuring that wide-character formatting paths remain distinct from the standard UTF-8 implementation. This separation prevents accidental mixing of narrow and wide character strings without explicit conversion.

## Practical Examples for Multiple Encodings

The following examples demonstrate encoding-specific formatting without manual conversion code:

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

// UTF-8 (default)
auto utf8_msg = fmt::format("Hello, {}!", "world");   // → std::string

// UTF-16 using a char16_t literal
auto utf16_msg = fmt::format(u"Number: {}", 42);     // → std::u16string

// UTF-32 using a char32_t literal
auto utf32_msg = fmt::format(U"Hex: {:#x}", 0xDEAD); // → std::u32string

// Wide-character string (platform-dependent encoding)
auto w_msg = fmt::format(L"Path: {}", L"/usr/local"); // → std::wstring

// Formatting a UTF-8 std::string into a UTF-16 result
std::string utf8_src = "π ≈ 3.14159";
auto utf16_res = fmt::format(u"Value: {}", utf8_src); // conversion performed internally

// Printing directly to a wide-character stream
fmt::print(stdout, L"Wide output: {}\n", L"✓");       // uses wformat_string overload

```

Note that [`include/fmt/xchar.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/xchar.h) is required for `wchar_t` support, while standard UTF-8 formatting only requires [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h).

## Summary

- fmtlib uses template parameter `Char` to abstract character encoding details in [`include/fmt/base.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/base.h).
- **UTF-8** formatting works by default with `char` strings via [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h).
- **UTF-16** and **UTF-32** formatting requires using `u""` or `U""` literals, automatically instantiating `char16_t` or `char32_t` specializations.
- The library includes an internal UTF-8 to UTF-16 converter (documented at line 1502 of [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)) for automatic transcoding.
- **Wide characters** (`wchar_t`) require [`include/fmt/xchar.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/xchar.h), which provides distinct overloads to prevent mixing with narrow strings.
- No explicit conversion functions are necessary; the API selects the correct encoding path based on the format string type.

## Frequently Asked Questions

### Does fmtlib require explicit conversion between UTF-8 and UTF-16?

No. The library handles conversion automatically when you pass UTF-8 arguments to UTF-16 format strings (or vice versa). As noted in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), the implementation includes an internal converter that manages the transcoding transparently during the formatting operation.

### How does fmtlib handle wchar_t differences between Windows and Linux?

The [`include/fmt/xchar.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/xchar.h) header provides `wchar_t` support that adapts to platform definitions—UTF-16 on Windows and typically UTF-32 on POSIX systems. The `vformat_to` function at line 64 includes a static assertion ensuring `wchar_t` formatting uses the dedicated wide-character code path rather than the standard `char` implementation.

### Can I mix UTF-8 arguments with UTF-16 format strings?

Yes. You can pass a `std::string` containing UTF-8 data to `fmt::format` with a UTF-16 format string (using the `u` prefix). The library performs the necessary conversion internally, returning a `std::u16string` result without requiring manual transcoding steps.

### Which header should I include for wide character formatting?

Include [`include/fmt/xchar.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/xchar.h) when working with `wchar_t` or `std::wstring`. This header defines `wformat_string` and the necessary overloads for `fmt::format` and `fmt::print` with wide characters. Standard UTF-8 formatting only requires [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h).