| layout | default |
|---|---|
| title | Getting started |
| parent | How-to guide |
| nav_order | 1 |
- Components are subclasses of
ViewComponent::Baseand live inapp/components. It's common practice to create and inherit from anApplicationComponentthat'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. (
AvatarComponentinstead ofUserComponent)
In Gemfile, add:
gem "view_component"Use the component generator to create a new ViewComponent.
The generator accepts a component name and a list of arguments:
bin/rails generate view_component: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.erbAvailable options to customize the generator are documented on the Generators page.
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
endContent 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>Since 2.31.0 {: .label }
String content can also be passed to a ViewComponent by calling #with_content:
<%= render(ExampleComponent.new(title: "my title").with_content("Hello, World!")) %>It's also possible to render ViewComponents in controllers:
def show
render(ExampleComponent.new(title: "My Title"))
endNote: Content can't be passed to a component via a block in controllers. Instead, use with_content.
When using turbo frames with turbo-rails, set content_type as text/html:
def create
render(ExampleComponent.new, content_type: "text/html")
endWhen 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
endTo 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)