diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index c9b233a5e..9ab9a2296 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -10,6 +10,10 @@ nav_order: 5
## main
+* Add `use_helper` API.
+
+ *Reegan Viljoen*
+
* Fix bug where the `Rails` module wasn't being searched from the root namespace.
*Zenéixe*
diff --git a/docs/guide/helpers.md b/docs/guide/helpers.md
index 1407fe14a..172c3aea0 100644
--- a/docs/guide/helpers.md
+++ b/docs/guide/helpers.md
@@ -51,6 +51,26 @@ class UserComponent < ViewComponent::Base
end
```
+## UseHelpers setter
+
+By default, ViewComponents don't have access to helper methods defined externally. The `use_helpers` method allows external helpers to be called from the component.
+
+To use the `use_helpers` method, include `ViewComponent::UseHelpers`.
+`UseHelpers` defines the helper on the component and is similar in use to using `delegate` on helpers.
+
+```ruby
+class UseHelpersComponent < ViewComponent::Base
+ include ViewComponent::UseHelpers
+ use_helpers :icon
+
+ erb_template <<-ERB
+
+ <%= icon :user %>
+
+ ERB
+end
+```
+
## Nested URL helpers
Rails nested URL helpers implicitly depend on the current `request` in certain cases. Since ViewComponent is built to enable reusing components in different contexts, nested URL helpers should be passed their options explicitly:
diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb
index f5a91ac11..6a2538c42 100644
--- a/lib/view_component/base.rb
+++ b/lib/view_component/base.rb
@@ -12,6 +12,7 @@
require "view_component/slotable"
require "view_component/translatable"
require "view_component/with_content_helper"
+require "view_component/use_helpers"
module ViewComponent
class Base < ActionView::Base
diff --git a/lib/view_component/use_helpers.rb b/lib/view_component/use_helpers.rb
new file mode 100644
index 000000000..32d13cd43
--- /dev/null
+++ b/lib/view_component/use_helpers.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module ViewComponent::UseHelpers
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def use_helpers(*args)
+ args.each do |helper_method|
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
+ def #{helper_method}(*args, &block)
+ raise HelpersCalledBeforeRenderError if view_context.nil?
+ __vc_original_view_context.#{helper_method}(*args, &block)
+ end
+ RUBY
+
+ ruby2_keywords(helper_method) if respond_to?(:ruby2_keywords, true)
+ end
+ end
+ end
+end
diff --git a/test/sandbox/app/components/use_helpers_component.html.erb b/test/sandbox/app/components/use_helpers_component.html.erb
new file mode 100644
index 000000000..f24542007
--- /dev/null
+++ b/test/sandbox/app/components/use_helpers_component.html.erb
@@ -0,0 +1,3 @@
+
+ <%= message %>
+
diff --git a/test/sandbox/app/components/use_helpers_component.rb b/test/sandbox/app/components/use_helpers_component.rb
new file mode 100644
index 000000000..375c07db6
--- /dev/null
+++ b/test/sandbox/app/components/use_helpers_component.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class UseHelpersComponent < ViewComponent::Base
+ include ViewComponent::UseHelpers
+
+ use_helpers :message
+end
diff --git a/test/sandbox/test/rendering_test.rb b/test/sandbox/test/rendering_test.rb
index 2f5069ea4..cd985beb6 100644
--- a/test/sandbox/test/rendering_test.rb
+++ b/test/sandbox/test/rendering_test.rb
@@ -1094,4 +1094,9 @@ def test_content_security_policy_nonce
assert_selector("script", text: "\n//\n", visible: :hidden)
end
+
+ def test_use_helper
+ render_inline(UseHelpersComponent.new)
+ assert_selector ".helper__message", text: "Hello helper method"
+ end
end