Getting started
Conventions
- Components are subclasses of
ViewComponent::Base
and live inapp/components
. It’s common practice to create and inherit from anApplicationComponent
that’s a subclass ofViewComponent::Base
. - Component names end in -
Component
. - Component module names are plural, as for controllers and jobs:
Users::AvatarComponent
- Name components for what they render, not what they accept. (
AvatarComponent
instead ofUserComponent
)
Installation
In Gemfile
, add:
gem "view_component"
Quick start
Use the component generator to create a new ViewComponent.
The generator accepts a component name and a list of arguments:
bin/rails generate component Example title
invoke test_unit
create test/components/example_component_test.rb
create app/components/example_component.rb
create app/components/example_component.html.erb
Available options to customize the generator are documented on the Generators page.
Implementation
A ViewComponent is a Ruby class that inherits from ViewComponent::Base
:
class ExampleComponent < ViewComponent::Base
erb_template <<-ERB
<span title="<%= @title %>"><%= content %></span>
ERB
def initialize(title:)
@title = title
end
end
Content passed to a ViewComponent as a block is captured and assigned to the content
accessor.
Rendered in a view as:
<%= render(ExampleComponent.new(title: "my title")) do %>
Hello, World!
<% end %>
Returning:
<span title="my title">Hello, World!</span>
#with_content
Since 2.31.0
String content can also be passed to a ViewComponent by calling #with_content
:
<%= render(ExampleComponent.new(title: "my title").with_content("Hello, World!")) %>
Rendering from controllers
It’s also possible to render ViewComponents in controllers:
def show
render(ExampleComponent.new(title: "My Title"))
end
Note: Content can’t be passed to a component via a block in controllers. Instead, use with_content
. In versions of Rails < 6.1, rendering a ViewComponent from a controller doesn’t include the layout.
When using turbo frames with turbo-rails, set content_type
as text/html
:
def create
render(ExampleComponent.new, content_type: "text/html")
end
Rendering ViewComponents to strings inside controller actions
When rendering the same component multiple times for later reuse, use render_in
:
class PagesController < ApplicationController
def index
# Doesn't work: triggers a `AbstractController::DoubleRenderError`
# @reusable_icon = render IconComponent.new("close")
# Doesn't work: renders the whole index view as a string
# @reusable_icon = render_to_string IconComponent.new("close")
# Works: renders the component as a string
@reusable_icon = IconComponent.new("close").render_in(view_context)
end
end
Rendering ViewComponents outside of the view context
To render ViewComponents outside of the view context (such as in a background job, markdown processor, etc), instantiate a Rails controller:
ApplicationController.new.view_context.render(MyComponent.new)