# How to Integrate spdlog Using Header-Only Mode

> Integrate spdlog using header-only mode by defining the SPDLOG_HEADER_ONLY macro. Learn how this simplifies your build process for the fast C++ logging library.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: how-to-guide
- Published: 2026-07-19

---

**To integrate spdlog using header-only mode, define the `SPDLOG_HEADER_ONLY` macro before including any spdlog headers, which causes the implementation files (the `*-inl.h` files) to be compiled directly into your translation units.**

The [gabime/spdlog](https://github.com/gabime/spdlog) repository supports a header-only configuration that eliminates the need to link against a precompiled library. This approach embeds all logging implementation code into your project's object files, simplifying build configuration and dependency management.

## How Header-Only Mode Works

spdlog's architecture separates interface declarations from implementations using a dual-header pattern. Public headers such as [`spdlog.h`](https://github.com/gabime/spdlog/blob/main/spdlog.h) and [`logger.h`](https://github.com/gabime/spdlog/blob/main/logger.h) contain only declarations and type definitions, while companion files ending in [`-inl.h`](https://github.com/gabime/spdlog/blob/main/-inl.h) (e.g., [`spdlog-inl.h`](https://github.com/gabime/spdlog/blob/main/spdlog-inl.h), [`logger-inl.h`](https://github.com/gabime/spdlog/blob/main/logger-inl.h)) hold the actual implementations.

In [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h) at approximately line 350, a conditional preprocessor block controls the inclusion of the implementation:

```cpp
#ifdef SPDLOG_HEADER_ONLY
#include "spdlog/spdlog-inl.h"
#endif

```

When you define `SPDLOG_HEADER_ONLY`, the public headers automatically include their matching [`-inl.h`](https://github.com/gabime/spdlog/blob/main/-inl.h) counterparts. This makes the library header-only by compiling all necessary code into each translation unit that includes the headers. Without this macro, the implementations reside in the compiled library (`src/*.cpp`), requiring you to link against `libspdlog.a` or `libspdlog.so`.

## Integration Steps

### Step 1: Copy the Header Files

Copy the `include/spdlog` directory from the repository into your project's source tree or add it to your compiler's include path. This folder contains the public API headers and the bundled copy of the `fmt` formatting library.

### Step 2: Define the SPDLOG_HEADER_ONLY Macro

You must define `SPDLOG_HEADER_ONLY` before any spdlog header is included. You can do this either via a compiler flag:

```bash
g++ -std=c++17 -DSPDLOG_HEADER_ONLY -I/path/to/spdlog/include main.cpp -o my_app

```

Or directly in your source code:

```cpp
#define SPDLOG_HEADER_ONLY
#include "spdlog/spdlog.h"

```

### Step 3: Manage the fmt Library Dependency

By default, spdlog uses a bundled version of the fmt library located in `include/spdlog/fmt/`. If your project already links against fmt externally, define `SPDLOG_FMT_EXTERNAL` to prevent symbol duplication:

```cpp
#define SPDLOG_HEADER_ONLY
#define SPDLOG_FMT_EXTERNAL
#include "spdlog/spdlog.h"

```

When using this option, ensure the external fmt headers are available in your include path and link against the fmt library.

### Step 4: Compile with C++11 or Later

spdlog requires a C++11-compliant compiler or newer due to its use of variadic templates, thread-local storage, and other modern language features. No additional libraries beyond the C++ standard library are required for linking.

## Code Examples

### Basic Console Logging

The following example demonstrates minimal setup for header-only console logging:

```cpp
#define SPDLOG_HEADER_ONLY
#include "spdlog/spdlog.h"

int main()
{
    spdlog::info("Header-only spdlog works!");
    spdlog::warn("Warning with value: {}", 42);
    return 0;
}

```

### Color Console Output

Color console sinks are also available in header-only mode through the [`stdout_color_sinks.h`](https://github.com/gabime/spdlog/blob/main/stdout_color_sinks.h) header and its implementation in [`stdout_color_sinks-inl.h`](https://github.com/gabime/spdlog/blob/main/stdout_color_sinks-inl.h):

```cpp
#define SPDLOG_HEADER_ONLY
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"

int main()
{
    auto console = spdlog::stdout_color_mt("console");
    console->info("Colored output in header-only mode");
    return 0;
}

```

### Asynchronous Logging with Thread Pool

Async loggers require the thread pool implementation from [`include/spdlog/details/thread_pool-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/thread_pool-inl.h), which is automatically included when `SPDLOG_HEADER_ONLY` is defined:

```cpp
#define SPDLOG_HEADER_ONLY
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"

int main()
{
    spdlog::init_thread_pool(8192, 1);
    auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>(
        "async_file", "logs/async.txt");
    async_file->info("Async logger using header-only mode");
    return 0;
}

```

### Using External fmt

When linking against a system-installed fmt library:

```cpp
#define SPDLOG_HEADER_ONLY
#define SPDLOG_FMT_EXTERNAL
#include "spdlog/spdlog.h"

// Ensure -lfmt is passed to the linker
int main()
{
    spdlog::info("Using external fmt library");
    return 0;
}

```

## Key Implementation Files

Understanding these source files helps when debugging or customizing header-only integration:

- **[`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h)**: The primary public header declaring the core API; contains the conditional include of [`spdlog-inl.h`](https://github.com/gabime/spdlog/blob/main/spdlog-inl.h) around line 350.
- **[`include/spdlog/spdlog-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog-inl.h)**: Implements the global logging functions (e.g., `spdlog::info`, `spdlog::debug`) when header-only mode is active.
- **[`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h) & [`logger-inl.h`](https://github.com/gabime/spdlog/blob/main/logger-inl.h)**: Define the `logger` class and its member function implementations.
- **[`include/spdlog/sinks/stdout_color_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/stdout_color_sinks.h) & [`stdout_color_sinks-inl.h`](https://github.com/gabime/spdlog/blob/main/stdout_color_sinks-inl.h)**: Provide color console output functionality.
- **[`include/spdlog/async.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/async.h)**: Exposes the asynchronous logging interface; includes [`thread_pool-inl.h`](https://github.com/gabime/spdlog/blob/main/thread_pool-inl.h) when needed.
- **[`include/spdlog/details/thread_pool.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/thread_pool.h) & [`thread_pool-inl.h`](https://github.com/gabime/spdlog/blob/main/thread_pool-inl.h)**: Implement the message queue and worker threads used by async loggers.
- **`cmake/spdlog_header_only.pc.in`**: pkg-config template useful for CMake-based projects that consume spdlog as a header-only dependency.

## Summary

- Define **`SPDLOG_HEADER_ONLY`** before including spdlog headers to enable header-only compilation.
- The implementation logic resides in `*-inl.h` files (such as [`spdlog-inl.h`](https://github.com/gabime/spdlog/blob/main/spdlog-inl.h) and [`logger-inl.h`](https://github.com/gabime/spdlog/blob/main/logger-inl.h)) that are automatically included when the macro is defined.
- Use **`SPDLOG_FMT_EXTERNAL`** when linking against an external fmt library to avoid duplicate symbols.
- Header-only mode requires only the `include/spdlog` directory and a C++11-compliant compiler; no separate library linking is necessary.
- All features—including async logging, color consoles, and file sinks—work identically in header-only mode.

## Frequently Asked Questions

### How do I switch from compiled library mode to header-only mode?

Remove the spdlog library from your linker flags (e.g., remove `-lspdlog` from your build command) and add `#define SPDLOG_HEADER_ONLY` before your first spdlog include. Ensure you are not linking against a previously compiled `libspdlog.a` to avoid duplicate symbol errors, as the implementation will now be compiled directly into your translation units.

### Does header-only mode affect compilation times?

Yes, compilation times typically increase because the compiler must parse and compile the spdlog implementation code in every translation unit that includes the headers. For small projects, this is negligible; for large codebases, consider using precompiled headers (PCH) or the compiled library mode to reduce build times.

### Can I use async loggers in header-only mode?

Absolutely. Include [`spdlog/async.h`](https://github.com/gabime/spdlog/blob/main/spdlog/async.h) and initialize the thread pool with `spdlog::init_thread_pool()`. The header-only mode automatically pulls in [`include/spdlog/details/thread_pool-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/thread_pool-inl.h), which contains the full implementation of the message queue and worker thread logic required for asynchronous logging operations.

### What is the difference between `SPDLOG_HEADER_ONLY` and `SPDLOG_FMT_EXTERNAL`?

`SPDLOG_HEADER_ONLY` controls how spdlog's own code is compiled—either as headers or requiring a compiled library. `SPDLOG_FMT_EXTERNAL` controls the formatting backend: when defined, spdlog uses your system's fmt library instead of its bundled copy. These macros are independent; you can use header-only mode with either the bundled fmt or an external one.