diff --git a/.github/workflows/pipeline-release.yml b/.github/workflows/pipeline-release.yml
index 89c5d8a4..ac89dc5f 100644
--- a/.github/workflows/pipeline-release.yml
+++ b/.github/workflows/pipeline-release.yml
@@ -14,11 +14,10 @@ jobs:
     - name: pack source
       id: run-pack
       run: |
-        file="CMakeLists.txt"
-        version=$(grep -oP 'msft_proxy\s+VERSION\s+\K[0-9]+\.[0-9]+\.[0-9]+' "$file")
+        version=$(grep -oP 'msft_proxy\s+VERSION\s+\K[0-9]+\.[0-9]+\.[0-9]+' CMakeLists.txt)
         git tag "$version"
         git push origin "$version"
-        tar -czf "proxy-$version.tgz" "proxy.h"
+        tar -czf "proxy-$version.tgz" proxy.h proxy_fmt.h
         echo "PRO_VER=$version" >> $GITHUB_OUTPUT
       shell: bash
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 77dd586b..2d195522 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,7 +10,7 @@ target_include_directories(msft_proxy INTERFACE $<BUILD_INTERFACE:${CMAKE_CURREN
 include(GNUInstallDirs)
 install(TARGETS msft_proxy
         EXPORT proxyConfig)
-install(FILES proxy.h
+install(FILES proxy.h proxy_fmt.h
         DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/proxy)
 install(EXPORT proxyConfig DESTINATION ${CMAKE_INSTALL_DATADIR}/proxy)
 export(TARGETS msft_proxy FILE proxyConfig.cmake)
diff --git a/README.md b/README.md
index 2a84a5a0..9a8e492d 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ If so, this library is for you.
 
 "Proxy" was created by Microsoft engineers and has been used in the Windows operating system since 2022. For many years, using inheritance was the main way to achieve polymorphism in C++. However, new programming languages like [Rust](https://doc.rust-lang.org/book/ch10-02-traits.html) offer better ways to do this. We have improved our understanding of object-oriented programming and decided to use *pointers* in C++ as the foundation for "Proxy". Specifically, the "Proxy" library is designed to be:
 
-- **Portable**: "Proxy" was implemented as a single-header library in standard C++20. It can be used on any platform while the compiler supports C++20. The majority of the library is [freestanding](https://en.cppreference.com/w/cpp/freestanding), making it feasible for embedded engineering or kernel design of an operating system.
+- **Portable**: "Proxy" was implemented as a header-only library in standard C++20. It can be used on any platform while the compiler supports C++20. The majority of the library is [freestanding](https://en.cppreference.com/w/cpp/freestanding), making it feasible for embedded engineering or kernel design of an operating system.
 - **Non-intrusive**: An implementation type is no longer required to inherit from an abstract binding.
 - **Well-managed**: "Proxy" provides a GC-like capability that manages the lifetimes of different objects efficiently without the need for an actual garbage collector.
 - **Fast**: With typical compiler optimizations, "Proxy" produces high-quality code that is as good as or better than hand-written code. In many cases, "Proxy" performs better than traditional inheritance-based approaches, especially in managing the lifetimes of objects.
diff --git a/docs/faq.md b/docs/faq.md
index 64315796..f68c3521 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -15,7 +15,7 @@
 
 ### <a name="what">What is "Proxy" and how does it work?</a>
 
-"Proxy" is a single-header, cross-platform C++20 template library for modern runtime polymorphism based on pointer-semantics. Similar with C++ [virtual functions](https://en.cppreference.com/w/cpp/language/virtual), it generates indirect functions behind the scenes at compile-time but does not require inheritance. It also has [GC](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science))-like behavior that allows different objects of the same `proxy` type to have different lifetime models without requiring runtime GC overhead.
+"Proxy" is a header-only, cross-platform C++20 template library for modern runtime polymorphism based on pointer-semantics. Similar with C++ [virtual functions](https://en.cppreference.com/w/cpp/language/virtual), it generates indirect functions behind the scenes at compile-time but does not require inheritance. It also has [GC](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science))-like behavior that allows different objects of the same `proxy` type to have different lifetime models without requiring runtime GC overhead.
 
 ### <a name="why-popular">Why is "Proxy" so popular?</a>
 
@@ -35,7 +35,7 @@ The fundamental abstraction of "Proxy" is called "facade". It is recommended for
 
 ### <a name="how-integrate">How to integrate "Proxy" into my project?</a>
 
-Since "Proxy" is a single-header library, you can simply navigate to the [latest release](https://github.com/microsoft/proxy/releases), download the source code, and include "proxy.h" in your project. Make sure your compiler version meets the [minimum requirements for compilers](../README.md#compiler-req). If your project has already integrated with [vcpkg](https://vcpkg.io/) or [conan](https://conan.io/), just search for the keyword "proxy" and install it. Thanks to the community that helped port "Proxy" to these platforms!
+Since "Proxy" is a header-only library, you can simply navigate to the [latest release](https://github.com/microsoft/proxy/releases), download the source code, and include "proxy.h" in your project. Make sure your compiler version meets the [minimum requirements for compilers](../README.md#compiler-req). If your project has already integrated with [vcpkg](https://vcpkg.io/) or [conan](https://conan.io/), just search for the keyword "proxy" and install it. Thanks to the community that helped port "Proxy" to these platforms!
 
 ### <a name="how-migrate">My existing project uses virtual functions. How should I migrate to "Proxy"?</a>
 
diff --git a/proxy.h b/proxy.h
index d47111f9..c39fe120 100644
--- a/proxy.h
+++ b/proxy.h
@@ -2545,7 +2545,6 @@ struct formatter<pro::proxy_indirect_accessor<F>, CharT> {
 }  // namespace std
 #endif  // __STDC_HOSTED__ && __has_include(<format>)
 
-#undef ___PRO_THROW
 #undef ___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE
 
 #endif  // _MSFT_PROXY_
diff --git a/proxy_fmt.h b/proxy_fmt.h
new file mode 100644
index 00000000..715a27e3
--- /dev/null
+++ b/proxy_fmt.h
@@ -0,0 +1,102 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#ifndef _MSFT_PROXY_FMT_
+#define _MSFT_PROXY_FMT_
+
+#include <string_view>
+#include <type_traits>
+
+#ifndef __msft_lib_proxy
+#error Please ensure that proxy.h is included before proxy_fmt.h.
+#endif  // __msft_lib_proxy
+
+#if FMT_VERSION >= 60100
+static_assert(fmt::is_char<wchar_t>::value,
+    "The {fmt} library must have wchar_t support enabled. "
+    "Include fmt/xchar.h before including proxy_fmt.h.");
+#else
+#error Please ensure that the appropriate {fmt} headers (version 6.1.0 or \
+later) are included before proxy_fmt.h.
+#endif  // FMT_VERSION >= 60100
+
+namespace pro {
+
+namespace details {
+
+template <class CharT> struct fmt_format_overload_traits;
+template <>
+struct fmt_format_overload_traits<char>
+    : std::type_identity<fmt::format_context::iterator(
+          std::string_view spec, fmt::format_context& fc) const> {};
+template <>
+struct fmt_format_overload_traits<wchar_t>
+    : std::type_identity<fmt::wformat_context::iterator(
+          std::wstring_view spec, fmt::wformat_context& fc) const> {};
+template <class CharT>
+using fmt_format_overload_t = typename fmt_format_overload_traits<CharT>::type;
+
+struct fmt_format_dispatch {
+  template <class T, class CharT, class FormatContext>
+  ___PRO_STATIC_CALL(auto, const T& self, std::basic_string_view<CharT> spec,
+      FormatContext& fc)
+      requires(std::is_default_constructible_v<fmt::formatter<T, CharT>>) {
+    fmt::formatter<T, CharT> impl;
+    {
+      fmt::basic_format_parse_context<CharT> pc{spec};
+      impl.parse(pc);
+    }
+    return impl.format(self, fc);
+  }
+};
+
+}  // namespace details
+
+namespace skills {
+
+template <class FB>
+using fmt_format = typename FB::template add_convention<
+    details::fmt_format_dispatch, details::fmt_format_overload_t<char>>;
+
+template <class FB>
+using fmt_wformat = typename FB::template add_convention<
+    details::fmt_format_dispatch, details::fmt_format_overload_t<wchar_t>>;
+
+}  // namespace skills
+
+}  // namespace pro
+
+namespace fmt {
+
+template <pro::facade F, class CharT>
+    requires(pro::details::facade_traits<F>::template is_invocable<
+        false, pro::details::fmt_format_dispatch,
+        pro::details::fmt_format_overload_t<CharT>>)
+struct formatter<pro::proxy_indirect_accessor<F>, CharT> {
+  constexpr auto parse(basic_format_parse_context<CharT>& pc) {
+    for (auto it = pc.begin(); it != pc.end(); ++it) {
+      if (*it == '}') {
+        spec_ = std::basic_string_view<CharT>{pc.begin(), it + 1};
+        return it;
+      }
+    }
+    return pc.end();
+  }
+
+  template <class FormatContext>
+  auto format(const pro::proxy_indirect_accessor<F>& ia, FormatContext& fc)
+      const -> typename FormatContext::iterator {
+    auto& p = pro::access_proxy<F>(ia);
+    if (!p.has_value()) [[unlikely]]
+        { ___PRO_THROW(format_error{"null proxy"}); }
+    return pro::proxy_invoke<false, pro::details::fmt_format_dispatch,
+        pro::details::fmt_format_overload_t<CharT>>(p, spec_, fc);
+  }
+
+ private:
+  std::basic_string_view<CharT> spec_;
+};
+
+}  // namespace fmt
+
+#endif  // _MSFT_PROXY_FMT_
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 31100a46..cd87094c 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -17,9 +17,17 @@ set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) # Disable GMock
 FetchContent_MakeAvailable(googletest)
 include(GoogleTest)
 
+FetchContent_Declare(
+  fmt
+  URL https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.tar.gz
+  URL_HASH SHA256=ac366b7b4c2e9f0dde63a59b3feb5ee59b67974b14ee5dc9ea8ad78aa2c1ee1e
+)
+FetchContent_MakeAvailable(fmt)
+
 add_executable(msft_proxy_tests
   proxy_creation_tests.cpp
   proxy_dispatch_tests.cpp
+  proxy_fmt_format_tests.cpp
   proxy_format_tests.cpp
   proxy_integration_tests.cpp
   proxy_invocation_tests.cpp
@@ -31,7 +39,7 @@ add_executable(msft_proxy_tests
   proxy_view_tests.cpp
 )
 target_include_directories(msft_proxy_tests PRIVATE .)
-target_link_libraries(msft_proxy_tests PRIVATE msft_proxy gtest_main)
+target_link_libraries(msft_proxy_tests PRIVATE msft_proxy gtest_main fmt::fmt)
 
 if(MSVC)
   target_compile_options(msft_proxy_tests PRIVATE /W4 /WX)
diff --git a/tests/proxy_fmt_format_tests.cpp b/tests/proxy_fmt_format_tests.cpp
new file mode 100644
index 00000000..8451f1e7
--- /dev/null
+++ b/tests/proxy_fmt_format_tests.cpp
@@ -0,0 +1,75 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#include <gtest/gtest.h>
+
+#if defined(__NVCOMPILER)
+#pragma diagnostic push
+#pragma diag_suppress inline_gnu_noinline_conflict
+#pragma diagnostic push
+#pragma diag_suppress code_is_unreachable
+#endif  // defined(__NVCOMPILER)
+#include <fmt/format.h>
+#include <fmt/xchar.h>
+#if defined(__NVCOMPILER)
+#pragma diagnostic pop
+#pragma diagnostic pop
+#endif  // defined(__NVCOMPILER)
+
+#include "proxy.h"
+#include "proxy_fmt.h"
+
+namespace proxy_fmt_format_tests_details {
+
+struct NonFormattable : pro::facade_builder::build {};
+
+static_assert(!std::is_default_constructible_v<fmt::formatter<pro::proxy_indirect_accessor<NonFormattable>, char>>);
+static_assert(!std::is_default_constructible_v<fmt::formatter<pro::proxy_indirect_accessor<NonFormattable>, wchar_t>>);
+
+struct Formattable : pro::facade_builder
+    ::support<pro::skills::fmt_format>
+    ::support<pro::skills::fmt_wformat>
+    ::build {};
+
+static_assert(std::is_default_constructible_v<fmt::formatter<pro::proxy_indirect_accessor<Formattable>, char>>);
+static_assert(std::is_default_constructible_v<fmt::formatter<pro::proxy_indirect_accessor<Formattable>, wchar_t>>);
+
+}  // namespace proxy_fmt_format_tests_details
+
+namespace details = proxy_fmt_format_tests_details;
+
+TEST(ProxyFmtFormatTests, TestFormat_Null) {
+  pro::proxy<details::Formattable> p;
+  bool exception_thrown = false;
+  try {
+    std::ignore = fmt::format("{}", *p);
+  } catch (const fmt::format_error&) {
+    exception_thrown = true;
+  }
+  ASSERT_TRUE(exception_thrown);
+}
+
+TEST(ProxyFmtFormatTests, TestFormat_Value) {
+  int v = 123;
+  pro::proxy<details::Formattable> p = &v;
+  ASSERT_EQ(fmt::format("{}", *p), "123");
+  ASSERT_EQ(fmt::format("{:*<6}", *p), "123***");
+}
+
+TEST(ProxyFmtFormatTests, TestWformat_Null) {
+  pro::proxy<details::Formattable> p;
+  bool exception_thrown = false;
+  try {
+    std::ignore = fmt::format(L"{}", *p);
+  } catch (const fmt::format_error&) {
+    exception_thrown = true;
+  }
+  ASSERT_TRUE(exception_thrown);
+}
+
+TEST(ProxyFmtFormatTests, TestWformat_Value) {
+  int v = 123;
+  pro::proxy<details::Formattable> p = &v;
+  ASSERT_EQ(fmt::format(L"{}", *p), L"123");
+  ASSERT_EQ(fmt::format(L"{:*<6}", *p), L"123***");
+}
