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

> Learn how to integrate fmtlib into your CMake project using add_subdirectory, find_package, or FetchContent. Master these 3 proven methods for seamless integration.

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

---

**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`](https://github.com/fmtlib/fmt/blob/main/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`](https://github.com/fmtlib/fmt/blob/main/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:

```cmake

# 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`:

```cmake
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:

```cmake

# 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`](https://github.com/fmtlib/fmt/blob/main/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:

```cmake
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`](https://github.com/fmtlib/fmt/blob/main/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}:

```cmake
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.