# How to Integrate fmtlib/fmt as a CMake Subproject Using add_subdirectory

> Integrate fmtlib fmt as a CMake subproject with add_subdirectory. Link against fmt::fmt or the header-only version for flexible C++ logging.

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

---

**Use `add_subdirectory()` to embed the fmt repository into your CMake build graph, then link your targets against either `fmt::fmt` for the compiled library or `fmt::fmt-header-only` for header-only usage.**

The `{fmt}` library provides fast, type-safe string formatting for C++17 and newer projects. Integrating `fmtlib/fmt` as a CMake subproject using `add_subdirectory()` vendors the source code inside your repository, guaranteeing reproducible builds while automatically inheriting the library's compile options and SIMD settings through the root [`CMakeLists.txt`](https://github.com/fmtlib/fmt/blob/main/CMakeLists.txt).

## Why Use add_subdirectory() for fmtlib?

Embedding fmt via `add_subdirectory()` treats the library as part of your build tree rather than an external dependency. According to the `fmtlib/fmt` source code, the root [`CMakeLists.txt`](https://github.com/fmtlib/fmt/blob/main/CMakeLists.txt) defines the `add_library(fmt ...)` and `add_library(fmt-header-only INTERFACE)` targets, which propagate include directories and compiler flags automatically. This method eliminates the need to manually copy headers or maintain separate installation steps, keeping the [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) API surface available immediately.

## Step-by-Step Integration Guide

### 1. Add the fmt Source Tree

Clone or copy the `fmtlib/fmt` repository into a subdirectory of your project, typically `third_party/fmt` or `external/fmt`. This directory contains the top-level [`CMakeLists.txt`](https://github.com/fmtlib/fmt/blob/main/CMakeLists.txt) that defines the build rules.

### 2. Configure Build Options (Optional)

Set CMake cache variables before the `add_subdirectory()` call to customize fmt's behavior. Key options defined in the root [`CMakeLists.txt`](https://github.com/fmtlib/fmt/blob/main/CMakeLists.txt) include:

- `FMT_INSTALL` – Set to `OFF` to prevent fmt from appearing in your project's install set (recommended for embedded usage).
- `FMT_PEDANTIC` – Enables extra compiler warnings for stricter code analysis.
- `FMT_WERROR` – Treats warnings as errors during compilation.
- `FMT_USE_CPP17` – Enables C++17-specific features in the library.

### 3. Import the Subproject and Link Targets

Call `add_subdirectory()` with the path to fmt, then link the exported targets to your executables or libraries. The target `fmt::fmt` links the compiled library (from `src/format.cc`), while `fmt::fmt-header-only` provides an interface target for header-only usage.

## Understanding the CMake Targets

The `fmtlib/fmt` [`CMakeLists.txt`](https://github.com/fmtlib/fmt/blob/main/CMakeLists.txt) defines two primary targets under the `fmt::` namespace:

- **`fmt::fmt`** – The compiled library target. Building this target compiles `src/format.cc` and links it statically or shared depending on the `BUILD_SHARED_LIBS` setting.
- **`fmt::fmt-header-only`** – An `INTERFACE` library that adds `include/` to your include directories. Use this when you prefer to include `<fmt/format.h>` directly without linking a separate binary.

Both targets automatically handle transitive dependencies and compiler feature requirements.

## Complete Working Example

Assume the following directory structure:

```text
my_project/
├── CMakeLists.txt
├── src/
│   └── main.cpp
└── third_party/
    └── fmt/
        ├── CMakeLists.txt
        ├── include/
        └── src/

```

Your top-level [`CMakeLists.txt`](https://github.com/fmtlib/fmt/blob/main/CMakeLists.txt) should contain:

```cmake
cmake_minimum_required(VERSION 3.14)
project(MyApp LANGUAGES CXX)

# Configure fmt options before importing

set(FMT_INSTALL OFF)
set(FMT_PEDANTIC ON CACHE BOOL "" FORCE)

# Integrate fmt as a subproject

add_subdirectory(third_party/fmt)

# Define your executable

add_executable(my_app src/main.cpp)

# Link against the compiled fmt library

target_link_libraries(my_app PRIVATE fmt::fmt)

# For header-only mode, use instead:

# target_link_libraries(my_app PRIVATE fmt::fmt-header-only)

```

Create [`src/main.cpp`](https://github.com/fmtlib/fmt/blob/main/src/main.cpp) with the following content:

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

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

```

Build the project:

```bash
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build .

```

The build system first compiles the fmt library (if you selected `fmt::fmt`), then builds your application with the correct include paths inherited from the subproject.

## Key Files in the fmt Repository

Understanding these files helps debug integration issues:

- **[`CMakeLists.txt`](https://github.com/fmtlib/fmt/blob/main/CMakeLists.txt)** (repo root) – Defines `add_library(fmt ...)` and the installation logic. It checks for `FMT_INSTALL` and sets compiler flags.
- **[`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)** – The primary header exposing `fmt::print`, `fmt::format`, and the core API.
- **`src/format.cc`** – The implementation file for the compiled library, compiled when using `fmt::fmt`.

## Summary

- Use `add_subdirectory(path/to/fmt)` to import the `fmtlib/fmt` build system into your CMake project.
- Link `fmt::fmt` for compiled library usage or `fmt::fmt-header-only` for header-only integration.
- Set variables like `FMT_INSTALL` and `FMT_PEDANTIC` before `add_subdirectory()` to control subproject behavior.
- The root [`CMakeLists.txt`](https://github.com/fmtlib/fmt/blob/main/CMakeLists.txt) handles all include directories and compiler flags automatically; no manual `target_include_directories()` is required.

## Frequently Asked Questions

### What is the difference between fmt::fmt and fmt::fmt-header-only?

`fmt::fmt` is a compiled library target that builds `src/format.cc` into a static or shared library, offering the fastest compile times for translation units that include fmt headers. `fmt::fmt-header-only` is an `INTERFACE` library that adds `include/` to your include path, causing the implementation to be compiled into every translation unit that uses it. Choose `fmt::fmt` for faster builds and `fmt::fmt-header-only` for simplicity in single-file projects.

### How do I disable fmt library installation when used as a subproject?

Set `set(FMT_INSTALL OFF CACHE BOOL "" FORCE)` before calling `add_subdirectory(third_party/fmt)`. This prevents the subproject from adding install rules to your project's export set, keeping your installation clean and focused on your own artifacts.

### Can I use fmt header-only mode without compiling the library?

Yes. Link against `fmt::fmt-header-only` instead of `fmt::fmt`. This target is defined as an `INTERFACE` library in the root [`CMakeLists.txt`](https://github.com/fmtlib/fmt/blob/main/CMakeLists.txt) and exposes the headers under `include/fmt/` without compiling `src/format.cc`. Note that this increases compile times for targets that include `<fmt/format.h>`.

### Where are the fmt public headers located in the repository?

The public API resides in `include/fmt/`, specifically [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) for the core formatting functions and [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) for lightweight includes. When using `add_subdirectory()`, these paths are automatically added to your target's include directories via the `fmt::fmt` or `fmt::fmt-header-only` interface properties.