How to Integrate {fmt} into a CMake Project: 3 Proven Methods

The {fmt} library exposes namespaced CMake targets (fmt::fmt for the compiled library and fmt::fmt-header-only for header-only use) that can be consumed via add_subdirectory, find_package, or FetchContent.

Integrating a modern C++ formatting library into your build system should be straightforward. The {fmt} library—hosted at fmtlib/fmt—provides a clean CMake configuration that makes adding it to CMake projects simple. This guide covers three integration methods, explains the available targets, and shows how to integrate {fmt} based on analysis of its official source code.

Available CMake Targets in {fmt}

The root CMakeLists.txt defines several well-structured targets designed for different use cases:

Target Type Purpose Source Location
fmt::fmt Compiled (static/shared) Core formatting library with compiled implementation add_library(fmt …) at line 96
fmt::fmt-header-only Interface library Header-only API with FMT_HEADER_ONLY=1 defined add_library(fmt-header-only INTERFACE) at line 54
fmt::fmt-c Compiled library C-compatible wrapper around the C++ API add_library(fmt-c src/fmt-c.cc) at line 60
fmt::fmt-module C++20 module Optional C++20 modules support add_module_library(fmt-module …) at line 37

All targets are exported with the fmt:: namespace, enabling consistent use across integration methods.

Why the Namespace Matters

The setup_target macro (lines 45–73 in CMakeLists.txt) centralizes common properties including include directories, version information, and symbol visibility. This ensures that linking against fmt::fmt automatically propagates all necessary compiler flags and paths, eliminating manual configuration.

Method 1: add_subdirectory Integration

Best for: Projects that vendor dependencies or maintain a monorepo structure.

When {fmt} lives inside your project's source tree, use add_subdirectory to pull its CMake configuration directly:


# Add {fmt} as a subdirectory

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/fmt)

# Link your executable or library

add_executable(my_app src/main.cpp)
target_link_libraries(my_app PRIVATE fmt::fmt)

This approach creates all targets in the current CMake scope. The fmt library is built as part of your project, and include paths are handled automatically through target_include_directories set by setup_target.

For header-only mode, substitute fmt::fmt-header-only:

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/fmt)
add_executable(my_app src/main.cpp)
target_link_libraries(my_app PRIVATE fmt::fmt-header-only)  # No compiled binary

Method 2: find_package Integration

Best for: System-wide or package manager installations.

After installing {fmt} (via make install, vcpkg, conan, or your system package manager), CMake generates fmt-config.cmake in the installation prefix. This enables standard package discovery:


# Locate installed {fmt} package

find_package(fmt REQUIRED)

# Link against the discovered target

add_executable(my_app src/main.cpp)
target_link_libraries(my_app PRIVATE fmt::fmt)

The export(TARGETS … NAMESPACE fmt:: …) logic at line 73 in CMakeLists.txt ensures that installed targets maintain the same namespaced interface as subdirectory builds. The support/cmake/fmt-config.cmake.in template generates the configuration file that find_package consumes.

Verifying Installation Paths

By default, {fmt} installs to standard CMake locations. The FMT_INSTALL option (defined around lines 87–94) controls whether installation rules are generated—defaults to ON when {fmt} is the main project.

Method 3: FetchContent Integration

Best for: CMake-only projects that want automatic source fetching without manual checkout.

FetchContent downloads {fmt} at configure time, making it ideal for CI/CD pipelines or contributor-friendly repositories:

include(FetchContent)

# Declare {fmt} source location

FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG        v10.2.1  # Pin to specific release

)

# Make target available in current scope

FetchContent_MakeAvailable(fmt)

# Link as usual

add_executable(my_app src/main.cpp)
target_link_libraries(my_app PRIVATE fmt::fmt)

FetchContent_MakeAvailable effectively wraps add_subdirectory with automatic download, providing identical target semantics. All configurable options like FMT_TEST, FMT_DOC, and FMT_MODULE can be set before the MakeAvailable call to customize the build.

Choosing Between Compiled and Header-Only Modes

Mode Target Trade-offs
Compiled fmt::fmt Faster compile times for translation units; requires linking; recommended for most projects
Header-only fmt::fmt-header-only No binary dependency; slower compile times; target_compile_definitions sets FMT_HEADER_ONLY=1 automatically

The header-only interface at line 56 explicitly defines FMT_HEADER_ONLY=1 through target_compile_definitions, ensuring consistent behavior without manual macro management.

Summary

  • Use fmt::fmt for the compiled library (default, best performance) or fmt::fmt-header-only for zero binary dependencies
  • Three integration paths work identically: add_subdirectory for vendored code, find_package for system installations, FetchContent for automatic fetching
  • All methods expose the same namespaced targets thanks to centralized setup_target logic and proper CMake export configuration
  • Key source files: CMakeLists.txt (targets and export), support/cmake/fmt-config.cmake.in (package template), include/fmt/*.h (public API)

Frequently Asked Questions

What is the difference between fmt and fmt::fmt?

The unqualified fmt is the internal CMake target name; fmt::fmt is the namespaced alias used after export. Always use fmt::fmt in your target_link_libraries calls—it works consistently across all three integration methods and protects against target name collisions.

Can I use {fmt} without modifying my system's installed packages?

Yes. The FetchContent or add_subdirectory methods require no system installation. FetchContent is particularly convenient as it downloads {fmt} automatically during CMake configuration with no manual steps.

How do I disable optional components like tests or documentation?

Set CMake options before integrating {fmt}:

set(FMT_TEST OFF CACHE BOOL "" FORCE)
set(FMT_DOC OFF CACHE BOOL "" FORCE)

# Then add_subdirectory, FetchContent_MakeAvailable, etc.

These options (defined lines 87–94) default to ON when {fmt} is the top-level project but can be overridden when consumed as a dependency.

Does {fmt} support C++20 modules in CMake?

Yes, when FMT_MODULE is enabled and the compiler supports C++20 modules, the fmt-module target provides modularized access to formatting facilities. This is an advanced feature requiring compatible toolchain configuration.

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 →