# SPDLog Header-Only vs Compiled Library: When to Use Each Variant

> Understand SPDLog header-only vs compiled library trade-offs. Choose the right variant for your project's compilation speed and binary size needs with identical performance.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: deep-dive
- Published: 2026-07-15

---

**SPDLog offers two build variants—a header-only mode that includes implementation directly from `include/spdlog/` and a compiled library mode that builds `libspdlog.a` or `libspdlog.so` from sources in `src/`—with identical runtime performance but different trade-offs in compilation speed and binary size.**

The `gabime/spdlog` repository supports dual consumption models through conditional compilation controlled by CMake targets. Understanding the distinction between these variants helps you optimize build times for large projects while maintaining flexibility for rapid prototyping.

## Build System Architecture and CMake Targets

The root [`CMakeLists.txt`](https://github.com/gabime/spdlog/blob/main/CMakeLists.txt) defines two distinct targets that determine how SPDLog is consumed. The `spdlog_header_only` target adds include directories and optionally links the bundled `fmt` header-only library, while the `spdlog` target compiles sources from `src/` into a physical library artifact.

When consuming the header-only variant, the `SPDLOG_HEADER_ONLY` macro is defined automatically by the CMake target. This macro causes [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h) to include implementation files directly rather than relying on external linkage. Conversely, the compiled variant leaves this macro undefined, expecting the linker to resolve symbols from the built library.

## Compilation Speed and Binary Size Trade-offs

### Header-Only Compilation Characteristics

Using the header-only variant eliminates the linking step entirely. You add `#include "spdlog/spdlog.h"` and compile with the include path set to the `include/` directory. However, every translation unit that includes SPDLog headers recompiles the full implementation, which can significantly inflate build times in codebases with many source files.

### Compiled Library Advantages

The compiled library variant produces `libspdlog.a` (static) or `libspdlog.so` (shared) from the implementation files in [`src/spdlog.cpp`](https://github.com/gabime/spdlog/blob/main/src/spdlog.cpp) and related source files. After the initial build, incremental compilation is faster because only changes to the library itself trigger recompilation. Additionally, shared libraries reduce total binary size when multiple executables link against SPDLog, as the object code is emitted only once.

## How to Configure the Header-Only Variant

Include the repository and link against the `spdlog_header_only` target. No library linking is required during the final executable build.

```cpp
// main.cpp
#include "spdlog/spdlog.h"

int main() {
    spdlog::info("Header-only SPDLog works!");
    return 0;
}

```

```cmake
cmake_minimum_required(VERSION 3.12)
project(HeaderOnlyDemo)

add_subdirectory(path/to/spdlog)
add_executable(demo main.cpp)
target_link_libraries(demo INTERFACE spdlog_header_only)

```

The header-only implementation is controlled in [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h), which conditionally includes the implementation based on the `SPDLOG_HEADER_ONLY` definition.

## How to Configure the Compiled Library Variant

Link against the `spdlog` target to consume the compiled library. This requires linking against the built library plus the `fmt` dependency unless `SPDLOG_FMT_EXTERNAL_HO` is enabled.

```cpp
// main.cpp
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"

int main() {
    auto logger = spdlog::basic_logger_mt("file_logger", "logs/app.log");
    logger->info("Compiled SPDLog library in action");
    return 0;
}

```

```cmake
cmake_minimum_required(VERSION 3.12)
project(CompiledDemo)

add_subdirectory(path/to/spdlog)
add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE spdlog)

```

On Unix systems, this translates to linking with `-lspdlog` at the command line. On Windows, you link against `spdlog.lib`.

## Implementation Details and File Structure

The core implementation resides in [`src/spdlog.cpp`](https://github.com/gabime/spdlog/blob/main/src/spdlog.cpp), which contains the template instantiations and registry logic. When building the compiled variant, these source files are compiled separately. When using the header-only variant, [`spdlog.h`](https://github.com/gabime/spdlog/blob/main/spdlog.h) includes these implementation details directly through conditional compilation directives.

The `cmake/spdlog_header_only.pc.in` file provides pkg-config support for header-only consumers who do not use CMake, ensuring the correct include flags are propagated to the compiler.

## Summary

- **Choose header-only** (`spdlog_header_only` target) for small programs, prototypes, or single-file distributions where build simplicity outweighs compile-time costs.
- **Choose compiled** (`spdlog` target) for large projects with multiple translation units to reduce incremental build times and share code via shared libraries.
- **Runtime performance is identical** between both variants because the generated machine code is the same; only the build process differs.
- **Switching variants** requires only changing the CMake target name from `spdlog` to `spdlog_header_only` (or vice versa) and adjusting the link interface from `PRIVATE` to `INTERFACE`.

## Frequently Asked Questions

### Does header-only SPDLog impact runtime performance?

No. The header-only and compiled variants produce identical runtime performance. The generated machine code is exactly the same because the header-only version uses the same source files found in `src/`; they are simply included at compile time rather than linked at link time.

### How do I switch between header-only and compiled modes in an existing project?

Change your CMake target link from `target_link_libraries(my_app PRIVATE spdlog)` to `target_link_libraries(my_app INTERFACE spdlog_header_only)`. Remove any explicit linking flags like `-lspdlog` from your build commands. The public API remains identical, so no source code changes are required.

### What is the difference between the `spdlog` and `spdlog_header_only` CMake targets?

The `spdlog` target compiles source files from `src/` into a static or shared library and defines `SPDLOG_HEADER_ONLY` as false. The `spdlog_header_only` target is an interface library that sets `SPDLOG_HEADER_ONLY` to true, causing headers under `include/spdlog/` to include their own implementations directly without producing a separate library file.

### Can I use SPDLog in a single translation unit without CMake?

Yes. For header-only usage without CMake, simply add the `include/` directory to your compiler's include path and compile a single file with `g++ -Ipath/to/spdlog/include main.cpp -std=c++11`. This works because the header-only variant requires no linking step, though you may need to link against the `fmt` library separately if not using the bundled header-only version.