diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml
index d539d49a55..38dda4ca3a 100644
--- a/.github/workflows/clippy.yml
+++ b/.github/workflows/clippy.yml
@@ -44,6 +44,8 @@ jobs:
         run:  cargo clippy -p sample_component_hello_world
       - name: Clippy sample_component_json_validator
         run:  cargo clippy -p sample_component_json_validator
+      - name: Clippy sample_component_json_validator_client
+        run:  cargo clippy -p sample_component_json_validator_client
       - name: Clippy sample_component_json_validator_winrt
         run:  cargo clippy -p sample_component_json_validator_winrt
       - name: Clippy sample_component_json_validator_winrt_client
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index ee18f28095..21eb5b12aa 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -68,6 +68,8 @@ jobs:
         run:  cargo test -p sample_component_hello_world --target ${{ matrix.target }} ${{ matrix.etc }}
       - name: Test sample_component_json_validator
         run:  cargo test -p sample_component_json_validator --target ${{ matrix.target }} ${{ matrix.etc }}
+      - name: Test sample_component_json_validator_client
+        run:  cargo test -p sample_component_json_validator_client --target ${{ matrix.target }} ${{ matrix.etc }}
       - name: Test sample_component_json_validator_winrt
         run:  cargo test -p sample_component_json_validator_winrt --target ${{ matrix.target }} ${{ matrix.etc }}
       - name: Test sample_component_json_validator_winrt_client
diff --git a/crates/samples/components/json_validator_client/Cargo.toml b/crates/samples/components/json_validator_client/Cargo.toml
new file mode 100644
index 0000000000..ca6dbe9b69
--- /dev/null
+++ b/crates/samples/components/json_validator_client/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "sample_component_json_validator_client"
+version = "0.0.0"
+edition = "2021"
+publish = false
+
+[build-dependencies]
+cc = "1.0"
+
+[dependencies.windows-targets]
+path = "../../../../crates/libs/targets"
+
+# TODO: this causes a warning about lack of linkage target. The point is to ensure that this binary dependency is built first but 
+# Cargo doesn't respect cdylib targets. https://github.com/rust-lang/cargo/issues/7825
+[dependencies.sample_component_json_validator]
+path = "../json_validator"
diff --git a/crates/samples/components/json_validator_client/build.rs b/crates/samples/components/json_validator_client/build.rs
new file mode 100644
index 0000000000..a669dceeb0
--- /dev/null
+++ b/crates/samples/components/json_validator_client/build.rs
@@ -0,0 +1,14 @@
+fn main() {
+    if !cfg!(target_env = "msvc") {
+        return;
+    }
+
+    println!("cargo:rerun-if-changed=src/client.cpp");
+    println!("cargo:rustc-link-lib=windows.0.52.0");
+
+    cc::Build::new()
+        .cpp(true)
+        .std("c++20")
+        .file("src/client.cpp")
+        .compile("client");
+}
diff --git a/crates/samples/components/json_validator_client/src/client.cpp b/crates/samples/components/json_validator_client/src/client.cpp
new file mode 100644
index 0000000000..b9ef1db984
--- /dev/null
+++ b/crates/samples/components/json_validator_client/src/client.cpp
@@ -0,0 +1,44 @@
+#include <stdint.h>
+#include <assert.h>
+#include <windows.h>
+#include <string_view>
+
+typedef HRESULT (__stdcall *CreateJsonValidator)(char const* schema, size_t schema_len, uintptr_t* handle);
+
+typedef HRESULT (__stdcall *ValidateJson)(uintptr_t handle, char const* value, size_t value_len, char** sanitized_value, size_t* sanitized_value_len);
+
+typedef void (__stdcall *CloseJsonValidator)(uintptr_t handle);
+
+extern "C" {
+    void __stdcall client() {
+        auto library = LoadLibraryExW(L"sample_component_json_validator.dll", 0, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+        assert(library != 0);
+
+        auto create = reinterpret_cast<CreateJsonValidator>(GetProcAddress(library, "CreateJsonValidator"));
+        assert(create);
+
+        auto validate = reinterpret_cast<ValidateJson>(GetProcAddress(library, "ValidateJson"));
+        assert(validate);
+
+        auto close = reinterpret_cast<CloseJsonValidator>(GetProcAddress(library, "CloseJsonValidator"));
+        assert(close);
+
+        std::string_view schema = "{\"maxLength\": 5}";
+        std::string_view json = "\"Hello\" "; // trailing space will be removed from sanitized result
+        std::string_view json_invalid = "\"Hello world\""; // this json is too long
+
+        uintptr_t validator = 0;
+        assert(S_OK == create(schema.data(), schema.size(), &validator));
+
+        char* sanitized_value = nullptr;
+        size_t sanitized_value_len = 0;
+        assert(S_OK == validate(validator, json.data(), json.size(), &sanitized_value, &sanitized_value_len));
+        std::string_view sanitized(sanitized_value, sanitized_value_len);
+        assert(sanitized == "\"Hello\"");
+        CoTaskMemFree(sanitized_value);
+
+        assert(E_INVALIDARG == validate(validator, json_invalid.data(), json_invalid.size(), &sanitized_value, &sanitized_value_len));
+
+        close(validator);
+    }
+}
diff --git a/crates/samples/components/json_validator_client/src/lib.rs b/crates/samples/components/json_validator_client/src/lib.rs
new file mode 100644
index 0000000000..6f1497b02a
--- /dev/null
+++ b/crates/samples/components/json_validator_client/src/lib.rs
@@ -0,0 +1,11 @@
+#![cfg(target_env = "msvc")]
+
+#[test]
+fn test() {
+    extern "system" {
+        fn client();
+    }
+    unsafe {
+        client();
+    }
+}