GitHub’s View Components
By: Jonathan Loos
Tags: Rails, Viewcomponents, Github, Ruby on Rails, Ruby

What are View Components

View components are Github’s stab at a more modern front end framework solution built specifically for rails. It focuses on code encapsulation to build reusable and testable UI components called View Components. They’re based off of the Presenter Pattern, which addresses bloated views and controllers by “creating a class representation of the state of the view”. What’s nice about VC’s is that they allow for logic that would typically live in the controller or view to live within them. Github describes these as ruby objects that output HTML, so when thinking of rendering a VC, think of it as instantiating an instance of that Component Class and accessing its methods. An important note to remember here is that a large portion of what will be covered here can also be accomplished using rails partials. The main benefit is that you can abstract away the large majority of a view’s logic into a class, reducing clutter in your .erb files.

Why use View Components?

Encapsulation

View Components provide encapsulated packages for everything your view needs, separating it from your controller and its logic. In other words, parameters are passed into View Components, but will not bleed in automatically from those set in the controller. This provides a clear source for the information, and removes the guesswork that comes with figuring out what is set where. Not to mention testing this way becomes MUCH nicer.

Explicit Initializer

View components implement a standard Ruby initializer that gives a more explicit requirement on what is needed to render. In other words, think of if I were to show you a file for a partial requiring 6 different variables. You can imagine how it can take some time to identify them all. View Components (at their base level) feature a .rb class file and a .html.erb template file. The class file defines the Ruby initializer with all parameters required for it to render, making it much easier to identify what parameters need to be passed in.

Speed

Github also points out that their View Components are ~10x faster than partials according to their benchmarks. This is particularly the case when in your production environments as Rails caches classes (or in other words VCs) rather than compiling the every use. The trick here is that class caching is disabled by default in development, but enabled in prod →

View component is significantly slower than partials · Issue #345 · github/view_component

When To Use View Components

One of the main reasons why you’d create a view component is to be able to reuse it. An easy example is a card where with libraries such as Bootstrap it’s pretty simple to reuse the same classes to achieve the same look for each one across your site. The complication arises when there’s custom logic, such as permissions and javascript that apply to these cards. VCs help here by encapsulating the logic in the class definition, and packaging asset files along with it. So in this scenario, we would create a card component that would take in a theme and some text. The class file can handle custom rendering, routing and more to alleviate the view. For example, this: view.html.erb

<div class="card">

<div class="card-body">

<% if user_can_do_this %>

<h5 class="card-title">Show this text!</h5>

<% elsif user_can_do_that %>

<h5 class="card-title">Show that text!</h5>

<% end %>

</div>

</div>

And you can imagine that you would still have to put this logic in a card partial or render the partial within each if-block which still keeps too much logic in the view. With VCs, this can be changed to:

class CardComponent < ViewComponent::Base

def initialize(:user)

if user.can_do_this

@title = "Show this text!"

elsif user.can_do_that

@title = "Show that text!"

end

end

end

Now, anytime you need to render that card just pass in the user and forget the rest. You can also imagine how much easier it is to test the CardComponent class and view separately, than it would be to test it all mushed together.

Layout

Collections

View Components have built in functionality to mimic Rails’ built in <%= render @users %> method which would go and render _user.html.erb for each user. So rather than throwing your card into a loop and iterating, you can extend the instantiation with <%= render (CardComponent.with_collection(@users)) %> . The cool thing here is our above code wouldn't have to change. VCs takes in one user at a time and renders it, so there's no need to handle the collection directly.

Content Areas

View Components take in parameters which helps with the simple case above, but what if it’s not just text we want to display within the tag? You can pass in parameters of any type to the initialize function, but what about entire HTML blocks? VCs allow for the definition of content areas or slots (experimental → https://viewcomponent.org/#slots-experimental) to help you layout components with more content. With our card example, we could define body and footer areas to support custom content.

<%= render(CardComponent.new(user: user) do |component| %>

<% component.with(:body) do %>

<p>My custom body.</p>

<% end %>

<% component.with(:footer) do %>

<p>My custom footer.</p>

<% end %>

<% end %> class CardComponent < ViewComponent::Base

with_content_areas :body, :footer

def initialize(:user)

if user.can_do_this

@title = "Show this text!"

elsif user.can_do_that

@title = "Show that text!"

end

end

end

Originally published at https://www.notion.so.