ActionComponent

A React-style component system for Ruby on Rails.

As your application gets bigger, you'll find you have components which are common to multiple pages. The normal Rails way is to use a render :partial, but you might have some logic and/or database setup you want to do for this partial. Short of putting them in every controller that uses the component, or even worse, putting them in the view, there's no elegant solution.

Enter ActionComponent. Encapsulate a component's setup logic and view in the same class, and render the component either from an existing Rails view, or straight from the controller.

While you can use Rails views to render your component's HTML, you can also use the JSX-like language directly in your component's Ruby code.

Installation

Add gem "action_component" to your Gemfile and run bundle. Done.

Examples

Example 1: Integrating components into a standard Rails app

<!-- app/views/posts/show.html.erb -->

<div class="post">
  <h2><%= @post.title %></h2>
  <%= render_component AuthorComponent, author: @post.author %>

  <%= simple_format @post.content %>
</div>
# app/components/author_component.rb

class AuthorComponent < ActionComponent::Base
  def load
    @post_count = @author.posts.published.count
  end

  def view
    div(class: 'author') do
      insert image_tag(@author, alt: @author.name)

      div link_to(@author.name, @author), class: 'name'

      div pluralize(@post_count, 'post'), class: 'post-count'

      stars
    end
  end

  def stars
    div class: 'stars' do
      @author.stars.times { span "*" }
    end
  end
end

Example 2: Using components instead of views

# app/controllers/posts_controller.rb

class PostsController < ApplicationController
  def show
    post = Post.find(params[:id])

    render_component PostComponent, post: post
  end
end
# app/components/post_component.rb

class PostComponent < ActionComponent::Base
  # You can specify which variables must be passed to this component and their
  # types (but only if you want to; by default it accepts everything.)
  required post: Post

  def view
    div(class: 'post') do
      h2 @post.title

      # This renders a div with a class of 'published-at'
      # Note the underscore has been replaced with a dash.
      e.div.published_at "Published at #{l @post.published_at}"

      component AuthorComponent, author: @post.author

      insert simple_format(@post.content)
    end
  end
end

More documentation to come

ActionComponent is new. It works just fine, but at the moment if you need more information than is given above, please dive into the (small) codebase to learn more.

Contributing

Pull requests welcome! If you're thinking of contributing a new feature, or significantly changing an existing feature, please propose it as an issue.

Licence

MIT. Copyright Roger Nesbitt.