Leveling up on Github View Components

Expand your use of Github's View Components gem for a more complete implementation.
David Nguyen
David Nguyen
July 14, 2023 (updated September 19, 2023)

Over the recent years, our team has wholeheartedly embraced Github's View Components within our codebase. Its flexibility, user-friendliness, and the array of advantages we highlighted in our previous blog have been the driving force behind our consistent utilization of View Components. During our most recent major refactor, View Components played a pivotal role in simplifying the creation of reusable views, proving to be an invaluable asset. As our team becomes more experienced with all of the features View Components has to offer, we can unlock its maximum potential.

Using View Component Previews

Similar to ActionMailer, View Component offers a feature that enables developers and designers to swiftly access an isolated preview of the component. The advantage of using this preview functionality is that developers can efficiently examine and iterate upon the component without the need to reload the entire page. This functionality can also be achieved by using Storybook JS, however as mentioned in our previous blog (link_to Jesse’s storybook post), it was not feasible to use with our application. Let's begin by exploring how to generate these previews. To start, you'll need to create a View Component.


# button_component.rb
Class ButtonComponent < ViewComponent::Base

def initialize(text:, name:)
  @text = text
  @name = name
end

# button_component.html.erb
<button class="btn btn-primary"> </button>

Now you will need to create a preview file.


# ./test/components/previews/button_component_preview.rb
class ButtonComponentPreview < ViewComponent::Preview

  def with_default_text(user_id: 1)
  name = User.find(user_id).name
    render(ButtonComponent.new(text: "Ping", name: name))
  end
end

Once added, you can go to http://localhost:3000/rails/view_components and select the component you would like to view. Query parameters can be added to the URL to dynamically change the component’s content. Unlike Storybook JS, View Component Previews can connect to your database and display your data.

View Component Slots

View Components not only accepts data through parameters but also accommodates blocks of content via slots. The advantage of utilizing slots is that it empowers you to create more versatile components while maintaining the DRY (Don’t Repeat Yourself) principle in your code.


# breadcrumb_component.rb
class BreadcrumbComponent < ViewComponent::Base
  renders_one :title
  renders_many :breadcrumb_items, BreadcrumbItemComponent
end

For slots that have multiple content blocks, you must loop through the list to render each content block. You can also check if a slot has been passed through by using the #{slot_name}? method.


# breadcrumb_component.html.erb

<div class="page-title-box">
  <div class="page-title-right">
    <ol class="breadcrumb m-0">
      <% breadcrumb_items.each do |breadcrumb_item| %>
        <%= breadcrumb_item %>
      <% end %>
    </ol>
  </div>
  <% if title? %>
    <h4 class="page-title"><%= title %></h4>
  <% end %>
</div>

# breadcrumb_item_component.rb

class BreadcrumbItemComponent < ViewComponent::Base
  def initialize(label:, path: nil)
    @label = label
    @path = path
  end
end

# show.html.erb

<%= render BreadcrumbComponent.new do |component| %>
    <% component.with_breadcrumb_item(label: "Home", path: root_path) %>
    <% component.with_breadcrumb_item(label: "Users", path: users_path) %>
    <% component.with_breadcrumb_item(label: @user) %>

  <% component.with_title do %>
    <%= @user.name %>
  <% end %>
<% end %>

When Not to Use Slots?

Incorporating slots into components may not be advantageous in cases where components are highly customizable or are so straightforward that slot usage is unnecessary. For instance, consider a button – it serves many different purposes, such as submitting a form or interacting with a Stimulus controller. Slots become more valuable when a component's functionality remains consistent across various contexts, as seen with breadcrumbs, where the primary function is simply linking the user to another page.

Interested in joining a mission-driven team committed to improving Canada? If so, please take a moment to look at our open positions!

About the author

David Nguyen

David is a Civil Engineer turned Full Stack and Lead Developer based out of Mississauga, ON.