diff --git a/CHANGELOG.md b/CHANGELOG.md
index 120dd62579b468747a2b4646a903ea6ff88e9748..cfe7ad694d9b777edb94b1aa1eb6c684e693d263 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -44,6 +44,9 @@ current (development)
 ### Screen
 - Feature: Add `Box::IsEmpty()`.
 
+### Util
+- Feature: Support arbitrary `Adapter` for `ConstStringListRef`. See #843.
+
 ### Build
 - Support for cmake's "unity/jumbo" builds. Fixed by @ClausKlein.
 
diff --git a/cmake/ftxui_test.cmake b/cmake/ftxui_test.cmake
index 0b9a86df333d7757e6f530201a3ed3d58ffe756f..c75ea4316c760433fa05097c59b265fb2d225212 100644
--- a/cmake/ftxui_test.cmake
+++ b/cmake/ftxui_test.cmake
@@ -18,6 +18,7 @@ add_executable(ftxui-tests
   src/ftxui/component/menu_test.cpp
   src/ftxui/component/modal_test.cpp
   src/ftxui/component/radiobox_test.cpp
+  src/ftxui/util/ref_test.cpp
   src/ftxui/component/receiver_test.cpp
   src/ftxui/component/resizable_split_test.cpp
   src/ftxui/component/screen_interactive_test.cpp
diff --git a/include/ftxui/util/ref.hpp b/include/ftxui/util/ref.hpp
index 15e03cf740c99d493b61722c8f963939abfe2106..42a5938149b77176c536102c3f08edc028287f9d 100644
--- a/include/ftxui/util/ref.hpp
+++ b/include/ftxui/util/ref.hpp
@@ -5,8 +5,10 @@
 #define FTXUI_UTIL_REF_HPP
 
 #include <ftxui/screen/string.hpp>
+#include <memory>
 #include <string>
 #include <variant>
+#include <vector>
 
 namespace ftxui {
 
@@ -104,42 +106,109 @@ class ConstStringRef : public ConstRef<std::string> {
 };
 
 /// @brief An adapter. Reference a list of strings.
+///
+/// Supported input:
+/// - `std::vector<std::string>`
+/// - `std::vector<std::string>*`
+/// - `std::vector<std::wstring>*`
+/// - `Adapter*`
+/// - `std::unique_ptr<Adapter>`
 class ConstStringListRef {
  public:
+  // Bring your own adapter:
+  class Adapter {
+   public:
+    Adapter() = default;
+    Adapter(const Adapter&) = default;
+    Adapter& operator=(const Adapter&) = default;
+    Adapter(Adapter&&) = default;
+    Adapter& operator=(Adapter&&) = default;
+    virtual ~Adapter() = default;
+    virtual size_t size() const = 0;
+    virtual std::string operator[](size_t i) const = 0;
+  };
+  using Variant = std::variant<const std::vector<std::string>,    //
+                               const std::vector<std::string>*,   //
+                               const std::vector<std::wstring>*,  //
+                               Adapter*,                          //
+                               std::unique_ptr<Adapter>           //
+                               >;
+
   ConstStringListRef() = default;
   ~ConstStringListRef() = default;
-  ConstStringListRef(ConstStringListRef&&) = delete;
-  ConstStringListRef& operator=(ConstStringListRef&&) = delete;
-  ConstStringListRef(const std::vector<std::string>* ref)  // NOLINT
-      : ref_(ref) {}
-  ConstStringListRef(const std::vector<std::wstring>* ref)  // NOLINT
-      : ref_wide_(ref) {}
-  ConstStringListRef(const ConstStringListRef& other) = default;
-  ConstStringListRef& operator=(const ConstStringListRef& other) = default;
+  ConstStringListRef& operator=(const ConstStringListRef&) = default;
+  ConstStringListRef& operator=(ConstStringListRef&&) = default;
+  ConstStringListRef(ConstStringListRef&&) = default;
+  ConstStringListRef(const ConstStringListRef&) = default;
+
+  ConstStringListRef(std::vector<std::string> value)  // NOLINT
+  {
+    variant_ = std::make_shared<Variant>(value);
+  }
+  ConstStringListRef(const std::vector<std::string>* value)  // NOLINT
+  {
+    variant_ = std::make_shared<Variant>(value);
+  }
+  ConstStringListRef(const std::vector<std::wstring>* value)  // NOLINT
+  {
+    variant_ = std::make_shared<Variant>(value);
+  }
+  ConstStringListRef(Adapter* adapter)  // NOLINT
+  {
+    variant_ = std::make_shared<Variant>(adapter);
+  }
+  template <typename AdapterType>
+  ConstStringListRef(std::unique_ptr<AdapterType> adapter)  // NOLINT
+  {
+    variant_ = std::make_shared<Variant>(
+        static_cast<std::unique_ptr<Adapter>>(std::move(adapter)));
+  }
 
   size_t size() const {
-    if (ref_) {
-      return ref_->size();
-    }
-    if (ref_wide_) {
-      return ref_wide_->size();
-    }
-    return 0;
+    return variant_ ? std::visit(SizeVisitor(), *variant_) : 0;
   }
 
   std::string operator[](size_t i) const {
-    if (ref_) {
-      return (*ref_)[i];
-    }
-    if (ref_wide_) {
-      return to_string((*ref_wide_)[i]);
-    }
-    return "";
+    return variant_ ? std::visit(IndexedGetter(i), *variant_) : "";
   }
 
  private:
-  const std::vector<std::string>* ref_ = nullptr;
-  const std::vector<std::wstring>* ref_wide_ = nullptr;
+  struct SizeVisitor {
+    size_t operator()(const std::vector<std::string>& v) const {
+      return v.size();
+    }
+    size_t operator()(const std::vector<std::string>* v) const {
+      return v->size();
+    }
+    size_t operator()(const std::vector<std::wstring>* v) const {
+      return v->size();
+    }
+    size_t operator()(const Adapter* v) const { return v->size(); }
+    size_t operator()(const std::unique_ptr<Adapter>& v) const {
+      return v->size();
+    }
+  };
+
+  struct IndexedGetter {
+    IndexedGetter(size_t index)  // NOLINT
+        : index_(index) {}
+    size_t index_;
+    std::string operator()(const std::vector<std::string>& v) const {
+      return v[index_];
+    }
+    std::string operator()(const std::vector<std::string>* v) const {
+      return (*v)[index_];
+    }
+    std::string operator()(const std::vector<std::wstring>* v) const {
+      return to_string((*v)[index_]);
+    }
+    std::string operator()(const Adapter* v) const { return (*v)[index_]; }
+    std::string operator()(const std::unique_ptr<Adapter>& v) const {
+      return (*v)[index_];
+    }
+  };
+
+  std::shared_ptr<Variant> variant_;
 };
 
 }  // namespace ftxui
diff --git a/src/ftxui/util/ref_test.cpp b/src/ftxui/util/ref_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e21dc56e8113aa9e4ebc29d5eefd5df36744a14c
--- /dev/null
+++ b/src/ftxui/util/ref_test.cpp
@@ -0,0 +1,63 @@
+#include "ftxui/util/ref.hpp"
+
+#include <gtest/gtest.h>
+#include "ftxui/component/component.hpp"
+
+namespace ftxui {
+namespace {
+class Adapter : public ConstStringListRef::Adapter {
+ public:
+  Adapter(std::vector<std::string>& entries) : entries(entries) {}
+  size_t size() const override { return entries.size() * 2; }
+  std::string operator[](size_t index) const override {
+    return entries[index / 2];
+  }
+  std::vector<std::string>& entries;
+};
+}  // namespace
+
+TEST(ConstStringListRef, Copy) {
+  std::vector<std::string> entries = {
+      "entry 1",
+      "entry 2",
+      "entry 3",
+  };
+  int selected = 0;
+  auto menu = Menu(entries, &selected);
+}
+
+TEST(ConstStringListRef, Ref) {
+  std::vector<std::string> entries = {
+      "entry 1",
+      "entry 2",
+      "entry 3",
+  };
+  int selected = 0;
+  auto menu = Menu(&entries, &selected);
+}
+
+TEST(ConstStringListRef, Adapter) {
+  std::vector<std::string> entries = {
+      "entry 1",
+      "entry 2",
+      "entry 3",
+  };
+
+  int selected = 0;
+  Adapter a(entries);
+  auto menu = Menu(&a, &selected);
+}
+
+TEST(ConstStringListRef, UniquePtrAdapter) {
+  std::vector<std::string> entries = {
+      "entry 1",
+      "entry 2",
+      "entry 3",
+  };
+
+  int selected = 0;
+  auto a = std::make_unique<Adapter>(entries);
+  auto menu = Menu(std::move(a), &selected);
+}
+
+}  // namespace ftxui