Skip to main content

Command Palette

Search for a command to run...

Why Ruby on Rails Needs Components

Updated
7 min read
Why Ruby on Rails Needs Components
A

I'm an engineer, educator, and entrepreneur that has been building digital products for over 20 years with experience in startups, education, technical training, and developer ecosystems.

I founded Flatiron School in NYC where I taught thousands of people how to code. Previously, I was the founder of Designer Pages. Currently I'm the Chief Product Officer at Revature.

I previously wrote a bit about What Rails Components and why don't we have them are at a very high level. Rails Components are shareable, encapsulated, and interoperable pieces of functionality that can be dropped into your Rails application. They are essentially the equivalent of React Components, styled, functional, interactive pieces of frontend that you can just drop into your application and they work.

Just as a reminder, the goal of the project is to enable this sort of equivalency:

import { Drawer } from 'vaul';

function MyComponent() {
  return (
    <Drawer.Root>
      <Drawer.Trigger>Open</Drawer.Trigger>
      <Drawer.Portal>
        <Drawer.Content>
          <p>Content</p>
        </Drawer.Content>
        <Drawer.Overlay />
      </Drawer.Portal>
    </Drawer.Root>
  );
}
<%= render_drawer do %>
  <%= drawer_trigger "Open" %>
  <%= drawer_content do %>
    <p>Content</p>
  <% end %>
<% end %>

Both would produce:

Drawer

But Why?

As I mentioned yesterday, solutions close to this exist in framework agnostic javascript libraries and potentially in native web components. I just don't think either of those are the right solution for Rails as javascript libraries still require potentially cumbersome integration and web components are introducing a new layer to your application. If Ruby on Rails is going to continue to grow in terms of adoption, it needs to get serious about enabling beautiful frontend experiences.

The benefit of Ruby on Rails is it's development speed which remains consistent as your application grows (to some extent, if you're building the next Github, you might run into some challenges). It's a great, if not the best, backend MVC framework out there. It was the original high-velocity web framework and quickly became, and to some extent probably still is, the best choice for the majority of content-based SaaS applications if not more. And that's because you can build those applications fast. But you can't make them look good fast. I mean you can, but that's your job and the framework doesn't help you.

A First Class Frontend

There's a lot of innovation and power in the Rails frontend, don't get me wrong. I think Hotwire, Turbo, and Stimulus are just incredible additions to the Rails ecosystem and provide the framework for building incredible frontend experiences. However, that's all they provide, the framework, they don't provide the experience. There are awesome libraries for Stimulus like Stimulus Components that use this framework to offer drop-in controllers that can provide common interactivity to your application. As long as you implement the markup and DOM as they intend, you can get these interactions.

<div data-controller="popover">
  This is my Github card available on
  <a href="/profile" data-action="mouseenter->popover#show mouseleave->popover#hide"> Github </a>

  <template data-popover-target="content">
    <div data-popover-target="card">
      <p>This content is in a hidden template.</p>
    </div>
  </template>
</div>

Popover

That's not bad. But, libraries like this, which I think are the most "drop in" friendly, still leave you to implement quite the specific markup. They also don't really come with styles and leave you to have a sense of how to style the elements, which is nice in terms of customizability but leaves something to be desired in terms of out of the box awesomeness.

What New Developers Want

Ultimately, as a new developer, when considering a web framework to use, I think a lot of them are seeing all the shiny components that React has to offer and thinking to themselves, "if I use React, my application can look and feel like that." I can't remember who recently said this, but shadcn/ui is almost a reason to use React and Next.js. It's that good of a base frontend. There's simply no equivalent concept in the Rails world. Point me to a Rails library that makes me think, this library alone justifies me using Rails. And you'd have to think of that just by looking at that library. After all, in the end of the day, it's easier to conceive of how you want your application to look and feel than how you want it to work and be implemented.

If Rails wants new developers, we need to give them a reason they can see to use Ruby on Rails.

Reasons You Can See to Use Ruby on Rails

Look, there's got to be more that we can offer to the frontend's markup than <%= content_tag %> or <%= form_for %>. ActionView is definitely a batteries not included framework. That's okay, that's what it intends to be, simple markup shortcuts for a frontend, not a frontend solution. You're suppose to use it to build your frontend, it is not suppose to provide you with a frontend. And that's the reason you can see that we need to provide the Ruby on Rails ecosystem: A complete markup solution for the frontend.

Providing a Frontend Solution

To scope this, what I mean by a complete markup solution for the frontend is the markup of your application along with styles provided by css classes, whether utility classes such as tailwindcss offers or utility classes such as bootstrap offers. There needs to be a mechanism, a strong pattern, that allows developers to share the idea of a fully styled component. To take a common example, imagine a Product Card, in fact, imagine this one from HyperUI.

Product Card

<a href="#" class="group relative block overflow-hidden">
  <button
    class="absolute end-4 top-4 z-10 rounded-full bg-white p-1.5 text-gray-900 transition hover:text-gray-900/75"
  >
    <span class="sr-only">Wishlist</span>
    <svg
      xmlns="http://www.w3.org/2000/svg"
      fill="none"
      viewBox="0 0 24 24"
      stroke-width="1.5"
      stroke="currentColor"
      class="h-4 w-4"
    >
      <path
        stroke-linecap="round"
        stroke-linejoin="round"
        d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z"
      />
    </svg>
  </button>
  <img
    src="https://images.unsplash.com/photo-1599481238640-4c1288750d7a?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2664&q=80"
    alt=""
    class="h-64 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-72"
  />

  <div class="relative border border-gray-100 bg-white p-6">
    <span
      class="whitespace-nowrap bg-yellow-400 px-3 py-1.5 text-xs font-medium"
    >
      New
    </span>

    <h3 class="mt-4 text-lg font-medium text-gray-900">Robot Toy</h3>

    <p class="mt-1.5 text-sm text-gray-700">$14.99</p>

    <form class="mt-4">
      <button
        class="block w-full rounded bg-yellow-400 p-4 text-sm font-medium transition hover:scale-105"
      >
        Add to Cart
      </button>
    </form>
  </div>
</a>

That's a lot of structure and markup for a card. What I've seen in Rails applications, what I think the framework would suggest, is to create something like a products/_card.html.erb partial. Now here's the interesting thing, let's look at what that view directory might look like:

products/
  _card.html.erb
  index.html.erb
  show.html.erb

Here you have two files that are clearly domain specific, the main views, responsible for how your application behaves to index products and to show a product. But then you have this partial that's probably not as domain specific, it's sort of generic, but it's in the same space as those other files. Another example could be more extreme.

account/
_account_dropdown.html.erb
artists/
  _artists_dropdown.html.erb
shared/
  _dropdown.html.erb

In this example, you have a domain specific _artists_dropdown.html.erb, presumably responsible for rendering a dropdown of artists. You also have a somewhat domain specific _account_dropdown.html.erb. And both of these use a shared generic _dropdown.html.erb. This is the pattern I think we need to change. We want to make _dropdown.html.erb a first class citizen in the Rails ecosystem. We want to make it a component and we want that to be easily shareable with another application through a gem or installer.

It might look like a cosmetic change, but what I'm suggesting is a pattern:


app/views/components
  _dropdown.html.erb

Files in there are generic UI units, first class objects in our view domain, that presumably can be copy and pasted from app to app or bundled and made available via a gem. You can render those partials using the standard render command. Those partials can be wrapped within ViewComponent or even Phlex architecture. Or they can be exposed by helpers that help normalize the options and the slots potentially required. Let's look at what a view helper for that product card partial might look like in use:

<%= render_product_card product_path(@product) do %>
  <%= product_title "Robot Toy" %>
  <%= product_price "$14.99" %>
<% end %>

I think that's compelling and clear. The API could change and certainly we can make the helper accept arguments for the content slots instead of representing the slots as capture areas in the block, but the point is, as a new developer I see that code and I see the end result I can get and it's a compelling reason to be using the framework. I can either inherit this themes helpers and views via a gem or install the helpers and views into my app via the gem so that I can edit them later.

This is what shadcn on rails aims to provide. As far as I know, it's the first gem that's saying it will install files into your app that are simply responsible for making structured components that come fully functional and fully styled. We need more of those if we want to capture the interests of new developers.

A

You are right Avi. Rails is missing this magic dust 🪄🎨 that empowers developers and add beauty to any Rails app. I've used yours and it is wonderful well written. As a developer with front-end and design experience, I started mine UI components to save myself time and work. And after a few weeks I launched Rapidrails UI as a product built on top on ViewComponent and TailwindCSS. While it is not an open source, I'm working on an free one, as a part of Rails Rails agency public kit.

I echo your voice, we need more Rails UI components.

N

Have you seen a Phlex though?

A

I totally have and love it. What this is about is a pattern for how we would share components, Phlex, ViewComponent, between applications and how we should create more component libraries that do that, regardless of the underlying implementation.

C

In Rails, a few gems add component functionality (like mountain_view). Unfortunately, the side effect of using components after some time is adding more specific logic in generic components.

A

I think you're right that it would be helpful to have a drop-in gem to provide some nice looking components. CSS frameworks like Bootstrap have helped with that in the past. I've been meaning to write a blog post about this, but the magic combination that has been working well for us has been Hotwire, Tailwind, and the ViewComponent gem. We've built our own components pretty easily and I'd recommend that stack for anyone who feels like components are lacking in Rails.

2
A

For sure, ViewComponents is great to build your own, I just want to be able to use yours too if you open sourced them and there's no real pattern or mechanism for that, you know?

A

Avi Flombaum Sure, I agree. I think the challenge is finding a way to share it while still allowing it to be customized. Tailwind UI component examples are easy because you just copy/paste and customize. However, with Rails/ViewComponents, you would probably want a Rails engine with a generator since there would be several files to copy/paste, or maybe you simply override the view template when you want to customize it. Btw, I created a "living styleguide" using ViewComponents that I haven't open-sourced yet. I think that's another important piece of the "Rails Components" story.

A

Andrew Havens Here is how I'm suggesting sharing them https://code.avi.nyc/rails-components-more-installer-over-library

A

Avi Flombaum The library is live at https://shadcn.rails-components.com you can install the gem in an app and generate components into it.

1
A

Andrew Havens When I started RapidRails library I had no plan to make is a gem, I want developers to copy and past files and folder. But, as the library grow and my goal is to make it unique I have several folders and files needs moving around. Maybe having a generator is an option. Still looking for a good solution.

For my custom clients work (deign to code) I create a few folders and developers copy/past, because it is unique for every project. How do you structure your library?

More from this blog

code.avi.nyc

23 posts

I'm an engineer, educator, and entrepreneur that has been building digital products for over 20 years with experience in startups, education, technical training, and developer ecosystems.