How to Use fmtlib as a Header-Only Library: Complete Setup Guide

To use {fmt} as a header-only library, link against the fmt::fmt-header-only CMake target or manually define the FMT_HEADER_ONLY macro before including the headers.

{fmt} is a modern C++ formatting library that provides fast, safe text formatting. While it traditionally builds as a compiled library, the fmtlib/fmt repository supports a header-only mode that eliminates the need to link against a separate binary, simplifying dependency management in projects where adding a compiled library is impractical.

Understanding Header-Only Mode

When you use fmtlib as a header-only library, all implementation code is provided inline through public headers rather than in a separate compiled archive. The source code uses conditional compilation to toggle between linked and inline implementations.

In include/fmt/format.h at line 1351 and include/fmt/core.h at line 2957, the library checks for the FMT_HEADER_ONLY macro:

#ifndef FMT_HEADER_ONLY
// … implementation that requires linking against the compiled library …
#endif  // FMT_HEADER_ONLY

When the macro is defined, these guards expose the inline implementation; otherwise, the code expects to link against the compiled libfmt.

The recommended way to enable header-only mode is through the fmt::fmt-header-only CMake target. This target is defined in the top-level CMakeLists.txt (lines 454-458) as an INTERFACE library:

add_library(fmt-header-only INTERFACE)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE cxx_std_11)
setup_target(fmt-header-only INTERFACE)

When you link against this target, CMake automatically propagates the FMT_HEADER_ONLY=1 definition and C++11 feature requirements to consuming targets. You do not need to define the macro manually in your source code.

Example CMake configuration:

cmake_minimum_required(VERSION 3.14)
project(myapp)

find_package(fmt REQUIRED)
add_executable(myapp main.cpp)

target_link_libraries(myapp PRIVATE fmt::fmt-header-only)

Method 2: Direct Macro Definition

If you are not using CMake, define the FMT_HEADER_ONLY macro manually before including any {fmt} headers. You can pass it as a compiler flag:

g++ -std=c++11 -DFMT_HEADER_ONLY -I/path/to/fmt/include main.cpp -o myapp

Alternatively, define it at the top of your source file:

#define FMT_HEADER_ONLY
#include <fmt/format.h>

int main() {
    fmt::print("The answer is {}.\n", 42);
    return 0;
}

Complete Usage Examples

CMake Project with Header-Only fmtlib

Create a CMakeLists.txt that finds the package and links to the header-only target:

cmake_minimum_required(VERSION 3.14)
project(myapp)

find_package(fmt REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt-header-only)

Your main.cpp requires no special configuration:

#include <fmt/core.h>

int main() {
    fmt::print("Hello, {}!\n", "world");
    return 0;
}

Manual Compilation Without Build Systems

For simple projects or single-file builds, compile directly with the macro defined:

g++ -std=c++11 -DFMT_HEADER_ONLY -I/path/to/fmt/include main.cpp -o myapp

The source code remains identical to standard usage:

#include <fmt/format.h>

int main() {
    fmt::print("The answer is {}.\n", 42);
    return 0;
}

Key Implementation Details

The header-only mode is validated by the test suite in test/header-only-test.cc, which ensures the library functions correctly when FMT_HEADER_ONLY is defined.

According to the source in CMakeLists.txt, the header-only target requires C++11 as the minimum standard. The macro guards in include/fmt/format.h (line 1351) and include/fmt/core.h (line 2957) control whether the implementation is pulled from the compiled library or expanded inline.

Summary

  • Define FMT_HEADER_ONLY to activate inline implementations in include/fmt/format.h and include/fmt/core.h.
  • Use fmt::fmt-header-only in CMake projects to automatically propagate the required compile definitions from CMakeLists.txt.
  • No linking required in header-only mode; the compiler processes all implementation code directly from headers.
  • C++11 minimum is required regardless of which integration method you choose.

Frequently Asked Questions

Does header-only mode affect compile times?

Yes. Using fmtlib as a header-only library typically increases compile times compared to the compiled version because the compiler must process the full implementation for every translation unit that includes the headers. The trade-off is simplified deployment without worrying about library paths or ABI compatibility.

Can I mix header-only and compiled modes in the same project?

No. You must choose one mode for your entire project. Defining FMT_HEADER_ONLY in some files but not others will cause One Definition Rule (ODR) violations and link-time errors, as the compiled library and inline implementations define the same symbols.

How do I know if my version supports header-only mode?

Header-only mode has been supported for multiple versions. The current implementation in CMakeLists.txt (lines 454-458) uses modern CMake INTERFACE target features. You can verify support by checking doc/get-started.md in your specific {fmt} release, which documents substituting fmt::fmt with fmt::fmt-header-only.

No. When using the header-only configuration, you do not need to link against libfmt or any additional libraries. The FMT_HEADER_ONLY macro ensures all necessary code is included inline from include/fmt/core.h and related headers, making the library truly self-contained.

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 →