fmtlib/fmt: a C++ formatting library that replaces printf and iostreams
A modern formatting library
At a glance
- What is it?
- fmt is a header-and-source C++ library that implements Python-style format strings, C++20 std::format and a safer printf. It is for teams who want type-safe, locale-independent formatting without dragging in a heavyweight dependency.
- Who is it for?
- Adopt fmt if you are writing C++ that formats numbers, dates, containers or user-facing messages and you want compile-time checking of format strings without pulling in a large dependency; the minimum configuration is three headers, core.h, format.h and format-inl.h, and the licence is MIT. Do not adopt it as a drop-in printf replacement for code that relies on locale-aware output, because the README states the library is locale independent by default.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What fmt replaces, and who feels the pain
C stdio and C++ iostreams both format text, and both have known sharp edges. printf is not type-safe: a mismatched specifier is undefined behaviour, not a compiler error. Iostreams are type-safe but verbose, slow for numeric work, and their manipulator state leaks across calls. fmt positions itself between them: a format API with positional arguments, a format string syntax similar to Python's format, and a documented implementation of C++20 std::format and C++23 std::print.
The audience is C++ developers who produce formatted output in more than one place and care about the cost of it. That includes people writing log lines, error messages, CLI output, serialization helpers, and anything that has to render numbers or timestamps consistently across platforms. The README also lists a safe printf implementation including the POSIX extension for positional arguments, which matters if you are migrating a large body of existing printf calls and cannot rewrite them all at once.
It is not aimed at projects that want zero build changes. fmt ships as source you compile or as a header-only configuration, so it is a build-system decision as much as a code decision.
How the format API and the Dragonbox float path work
The core mechanism is a format string plus a variadic argument list. fmt parses the string at the call site, and in C++20 it can check the specifiers against the argument types at compile time. The README gives the example of fmt::format("{:d}", "I am not a number"), which produces a compile-time error because d is an invalid format specifier for a string. That check is the main structural difference from printf: the failure moves from runtime to build time.
Positional arguments are a separate mechanism. fmt::format("I'd rather be {1} than {0}.", "right", "happy") yields "I'd rather be happy than right." The README notes this exists for localization, since a translator can reorder placeholders without touching the argument list.
For floating point, the README states the library uses the Dragonbox algorithm and provides correct rounding, shortness and round-trip guarantees. Round-trip means a formatted double parses back to the same value. Shortness means you get the fewest digits that still round-trip, which is why fmt output for doubles often looks shorter than iostreams output at default precision.
Beyond the core header, features are split across optional headers. fmt/chrono.h handles dates and times, fmt/ranges.h formats containers, fmt/color.h handles colors and text styles, and fmt/os.h provides fmt::output_file. Each is a separate include, and each carries its own compile cost.
Installing fmt and formatting your first value
The repository is built with CMake; CMakeLists.txt sits at the top level alongside include/, src/ and test/. The README points to https://fmt.dev for documentation and does not print a package-manager command, so the build path below is the one the repository layout supports.
A typical configure and build looks like this. The commands create a build directory, configure with CMake, and compile the library target.
cmake -S . -B build
cmake --build buildAfter that, a consumer links the fmt target. The README's first example is the smallest thing you can compile against the library.
#include <fmt/core.h>
int main() {
fmt::print("Hello, world!\n");
}Running that binary prints Hello, world! on stdout. To get a string back instead of printing, the README gives fmt::format("The answer is {}.", 42), which returns "The answer is 42.".
If you would rather not compile the library at all, the README documents an optional header-only configuration enabled with the FMT_HEADER_ONLY macro. Defining that macro before including fmt changes the build shape, at the cost of compiling the implementation into every translation unit that includes it.
The minimum configuration the README describes consists of just three files: core.h, format.h and format-inl.h. Anything beyond that, chrono, ranges, color, the file API, is opt-in by header.
Where fmt is the wrong tool
The README states the library is locale independent by default. That is a deliberate design choice and it is also the clearest case where fmt is the wrong tool. If your program must print a number with the user's thousands separator, or format a date according to a system locale, fmt's default output will not do it, and the README does not present locale-aware formatting as the primary path. Code that depends on that behaviour should stay on its current formatter or handle the locale conversion around fmt.
The compile-time format string check is also conditional. The README says the fmt::format("{:d}", "I am not a number") example gives a compile-time error in C++20. That qualifier matters: on an older standard, or through a code path where the format string is not a constant expression, you fall back to runtime checking. A team that adopts fmt specifically for compile-time safety should confirm which of their call sites actually get it.
There is a build-cost trade-off too. The README frames small code size as a feature, with a three-file minimum, but the optional headers are separate for a reason. Pulling in fmt/chrono.h and fmt/ranges.h across a large codebase is a different proposition from including fmt/core.h in one translation unit. Header-only mode with FMT_HEADER_ONLY moves that cost into every translation unit that defines it.
Finally, fmt is a formatting library, not a logging framework. If what you actually need is log levels, sinks, rotation and asynchronous dispatch, fmt is a component of that stack rather than a replacement for it.
fmt against std::format and against printf
The most direct alternative is std::format itself, since fmt documents an implementation of C++20 std::format and C++23 std::print. If your toolchain already provides std::format, the difference narrows to availability and reach: fmt supports older compilers and consistent output across platforms, per the README's portability section, while std::format depends on how far along your standard library is. For a codebase pinned to an older standard, fmt is the way to get the API before the standard library ships it. For a codebase already on a recent toolchain with a complete implementation, adding fmt is a choice about portability and about the extra features (chrono, ranges, color, output_file) rather than about the core API.
The other alternative is staying on printf. The README positions fmt as a fast and safe alternative to C stdio. The practical difference is where errors surface: printf defers a mismatched specifier to undefined behaviour at runtime, while fmt's format string can be checked at compile time in C++20. fmt also ships a safe printf implementation with the POSIX positional-argument extension, which is the migration path for code that cannot be rewritten immediately. Choosing printf means keeping the runtime risk; choosing fmt means a build-system change and, if you want the compile-time checks, a C++20 baseline for those call sites.
Maintenance, releases and the MIT licence
The repository is not archived, and the last push was on 2026-09-19. The most recent release listed is 12.2.0 on 2026-06-16, preceded by 12.1.0 on 2025-10-29 and 12.0.0 on 2025-09-17. The 12.x line has moved roughly twice a year, and the jump from 12.0.0 to 12.1.0 is a minor-version bump, which is the kind of release that can carry behaviour changes for code that leans on formatting details. Pin a version and read ChangeLog.md before moving across a minor boundary; the file is at the top level of the repository.
The upgrade cost is mostly compile time and header surface. Because features are split across fmt/core.h, fmt/chrono.h, fmt/ranges.h, fmt/color.h and fmt/os.h, the set of headers a translation unit includes determines how much it recompiles when the library changes. Teams that use FMT_HEADER_ONLY pay that cost in every translation unit that defines the macro, which makes the header-only configuration a poor fit for large projects that upgrade often.
The licence is MIT, per the repository's LICENSE file and the README's feature list, which describes the library as having no external dependencies and a permissive MIT license. MIT is permissive and imposes no copyleft obligation on your code, but this is a description of the licence text, not legal advice; read LICENSE and your own organisation's policy before shipping.
Editorial conclusion
Adopt fmt if you are writing C++ that formats numbers, dates, containers or user-facing messages and you want compile-time checking of format strings without pulling in a large dependency; the minimum configuration is three headers, core.h, format.h and format-inl.h, and the licence is MIT. Do not adopt it as a drop-in printf replacement for code that relies on locale-aware output, because the README states the library is locale independent by default. Before committing, verify two things yourself: that your compiler and standard library combination gives you the compile-time format string checks you expect, and that the three-file minimum configuration actually covers the features you use, since ranges, chrono, color and the file API each live in separate headers.
Frequently asked questions
What is the fmt library?
fmt is an open-source C++ formatting library that the README describes as a fast and safe alternative to C stdio and C++ iostreams. It provides a format API with positional arguments, a format string syntax similar to Python's format, and documented implementations of C++20 std::format and C++23 std::print.
Is the fmt package efficient?
The README states that fmt minimizes dynamic memory allocations and can optionally compile format strings into efficient formatting code, and it claims speed advantages over sprintf and iostreams, especially for numeric formatting. It points to the format-benchmark and dtoa-benchmark repositories for the benchmarks and methodology.
What is an fmt file?
The README does not describe an fmt file format. fmt here is a C++ formatting library, not a file type, and no file extension is mentioned in the repository description.
What is the fmt package in Go?
The repository covers fmtlib/fmt, the C++ library, and says nothing about a Go package of the same name. The README's examples are all C++.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fmtlib-fmt)