Reusable UI components in Rails

A component-based approach for organizing view partials

Filipe W. Lima
Goiabada
10 min readOct 10, 2017

--

Have you ever worked in a web application with many custom user interface (UI) components? How were they structured? In this article, I will share some tips and tricks on how to organize them in a Ruby on Rails application.

In the front-end development world, a component-based approach for building web applications has been gaining popularity since the spread of libraries and frameworks such as React and Vue for JavaScript. In React applications, components manage their own state and auto-update when data changes. They are then composed together to build complex UIs.

Prior to that, many CSS methodologies such as SMACSS, BEM, and Atomic Design, were created to help organizing the styles in a sane way. Usually by splitting them into modular components, elements, and their variations.

In Rails applications, pages are organized in layout and view files that can be further broken down into more manageable and reusable chunks, called partials. Due to their limited server-side rendering nature, expressing components in Rails applications is not as powerful as with modern JavaScript libraries. However, there can still be a lot of benefits from structuring them, especially on medium and large projects with nonconventional components:

  • Consistent use: each component exposes a public interface that is used to render itself in different ways, depending on the given parameters. This helps to avoid cases of misspelled CSS classes or misplaced HTML elements.
  • Easier maintenance: each component definition is in a distinct file, so there's a single source of truth. This helps with maintenance since there is only one place you need to modify to update them all.
  • Abstraction from implementation details: complex components with a heavy HTML structure and the right combination of CSS classes can be abstracted into simple function calls that hide unnecessary details.

The usual way for a developer to reuse a component is by copying it from anywhere it is already being used. Then pasting in the desired location and doing some tweaking to match the current needs. This manual process is error-prone, especially if the developer is not familiar with how everything is organized inside the component (e.g., which particular classes to use in different states) or is not fond of front-end technologies (some back-end developers consider CSS to be a black magic box).

In order to demonstrate how to apply the following tips, I will use some components from Twitter’s Bootstrap, a popular front-end library. This kind of library is very effective for quickly setting up a new application's UI. Therefore, it is common to find them being used in Rails applications as the base structure with overridden styles and custom themes.

By the end of this article, you will learn how to create specific helpers for rendering components of your application, such as this alert:

Danger alert to display the errors for a blog application.

With the fundamentals, you can expand the ideas to build more complex components such as cards, modals, or whatever your application needs.

Identify reusable components

The first step is to identify which elements are worth turning into components, as not everything is worth the time and effort.

Avoid going too far as turning simple things like buttons, links, headings, or other single elements into components. In these cases, it’s easier to just use the HTML version with classes.

If your project does not utilize something like a components style guide, you can use other ways to identify them. You can try to detect similar components being reused while browsing your codebase and start with the ones that are used the most or are more complex.

Front-end style guides are a modular collection of all the elements in your product’s user interface, together with code snippets for developers to copy and paste as needed to implement those elements. They include common UI components like buttons, form-input elements, navigation menus, modal overlays, and icons.

Lean UX by Jeff Gothelf and Josh Seiden

For example, form elements are heavily used in most applications and can be easily simplified if you use gems like Simple Form or Formtastic. These tools allow your inputs to be easily customized so you don't have to manually include the labels, repeat the same CSS classes every time, or deal with displaying validation errors. Consider organizing your form fields using these gems, or simply create your own form builder with Rails.

Directories structure

The recommended directory for placing partials that are shared with more than one view is app/views/application/. Although the components would also fit in that category, I'd rather recommend for them to be stored in a more specific directory, such as app/views/components/, because the application directory may contain other reusable partials that are not considered components.

Complex components with multiple inner elements, or that belong to the same category, can be grouped together in their own directories:

Public Interface

The component’s API is the public interface it exposes to the clients. By clients here I mean the developers that are going to consume/render the component.

What is the simplest way your component can be rendered? What is the minimum number of required parameters? Which of them are optional and what values do they fall back on?

Check out the documentation on Action View Partials for a detailed explanation on the multiple ways of rendering a partial.

In the case of our alert component, the most essential parameter is the message to be displayed:

The simplest way to render the alert.

Or something more flexible:

This way is more flexible for using HTML elements.

Here is the simplest implementation of the above:

The first implementation of our alert component.

We want the flexibility of using either a block or a parameter for the message. If a block is used, we use its value (stored in yield). Otherwise, we try to fetch it from the message parameter.

In this particular case of rendering a partial with a block without the layout option, we cannot simply check that a block has been used with block_given? inside the partial. It returns true and yield returns "". That's why we check if the block has a real content with presence.

All the parameters passed along when rendering the partial are stored in the local_assigns hash and transformed into local variables. We can use this hash to help define required and optional parameters.

Required parameters

If the client does not provide the expected parameters used within the partial, it will fail to render because it will be considered undefined. A better error strategy, in this case, is to use the Hash#fetch method. It will throw an error if the key is not provided:

Sometimes it might not make sense to render the component at all unless some condition is met. In such cases, we can use a guard clause and return earlier from our component.

We can verify whether the alert’s message is either nil or an empty string "" using Rails' blank method:

Return earlier if there’s no message.

Optional parameters

These parameters help to modify the component in different ways. They can be used to style it differently, include inner elements, apply different behaviors, you name it.

You must check for their keys' presence and fall back to some default value (or just nil) if they are not present. That can be done either using the || operator or Hash#fetch. I prefer the latter since it works better for boolean values.

Let's see how we can enable our alerts to be dismissible by default:

Implementation for dismissible alerts.

Since the parameters are optional, there is no need to modify existing render calls when adding them. These work great for expanding the component without affecting the existing clients.

Documentation

There isn't a built-in way to document the partials, so I rely on the ERB comment tag and the concept of tags from YARD (a superset of RDoc) to write the documentation for the components. This way, clients of your component will have an easier time figuring out how to use it.

Below is an example of how I would document the alert component:

Custom documentation for the component’s parameters.

Here I'm describing two parameters:

  1. message should be a String and is a required parameter (as expressed by the *, commonly used in web forms to denote obligatory fields).
  2. dismissible should be a Boolean, and it defaults to true.

The structure is simple: the component name with a brief description, followed by some YARD tags describing the parameters.

Refer to YARD's tags documentation to get an idea of how to use them. There are explicit ways to describe types such as arrays and hashes (useful for options). The most interesting tags for me are @param, @return, @option, @example and @yield.

Style variations

Our alert component is currently set to be styled only as a primary alert, but there are many possible different color styles it can use. We can introduce a new optional type parameter to enable different styles.

Success styled alert.

When the type param is not provided, we can default its value to the primary style:

Default to the primary style.

If we want, we can also enforce the value to be one among a list of possible valid values and throw an error otherwise:

Enforce valid style types.

In this specific case, the type value maps directly to the expected CSS classes (e.g., :primary => .alert-primary, :success => .alert-success, etc). However, this mapping might not always be so straightforward like this. In such situations, we can use a hash to create our desired mapping:

Map types into specific CSS classes.

Finally, we can use the resulting class in the component:

Apply different style classes depending on the type.

Inner elements

We might want to conditionally display some internal elements for our component.

Our alert component can display a heading text if one is provided:

Render an alert with a heading.

Which can be implemented as simply as:

Default the heading to nil unless it is provided. Else, render it.

In the above example, we used a simple string to specify the heading, but if we need something more elaborate we can use the capture helper to store a block with normal HTML in a variable:

Use the capture helper to pass a block of elements.

Finally, here is the final version for our alert component:

The final version for the alert component.

Although it got quite long because of all the rendering setup, it does the job. What I personally don’t like about this approach is the excess of logic required, mixed inside the view file. I prefer my views to be as logic-less as possible. One way to remove the logic from inside the partial is to use custom Rails helpers.

Helpers

Some components, or behaviors triggered by JS, can be applied just by adding some extra attributes to an existing element. Bootstrap’s tooltips and popovers are examples of that. With some helper methods, we can simplify the way they're used in our views.

Use of tooltip and popover helpers.

With the help of Ruby’s double splat operator **, we can merge the component's options hash returned by the helpers with the options hash from the tag helper. This way, you only need the bare minimum to use them.

You can also use helpers to create shortcut versions for the render component calls:

Generic helper for specifically rendering components.

Or you can be more explicit and shorter:

Specific render helper for the component.

Advanced use cases

In this article, I showed a couple of ways you can organize your components using only Rails partials and helpers. Although they work for simpler components, as previously explained, the partials can get quite cluttered with the excess of rendering logic.

It would be much better if we could encapsulate all the logic we need in a simple PORO (Plain Old Ruby Object) and use it inside our partial, without leaking implicit dependencies. Fortunately, there is a way: the Cells gem.

This gem was born out of frustration with Rails’ view layout. A cell is an object that represents a fragment in the UI, or the whole view, and can render a template.

In this gist, you can check out how the alert component would be implemented with Cells. Although considerably more verbose, it does a better job in separating the data and the presentation layers. Besides a more organized structure, this gem offers a lot more interesting features such as working with collections, nesting, context options, caching, and much more.

Let me know if you're interested to hear about Cells in a follow-up article!

Conclusion

Prior to working with Rails applications, I had the opportunity to work on a project to develop a front-end components library, similar to Twitter’s Bootstrap. This experience has given me insights on how to use these concepts in my views with Rails.

Today, when I'm developing an application, I am constantly looking for opportunities to extract reusable components in order to make life easier for me and other developers. As not many back-end developers are familiar with HTML & CSS, this way of rendering components is much simpler for them to use. Your users will also thank you for maintaining a consistent interface, as it will help them to gain familiarity and confidence with your application.

I hope this article has given you some ideas on how you can better organize your views around UI components, or just your regular partials. It may feel like overkill for simple components, but it surely pays off for more complex ones.

--

--

Filipe W. Lima
Goiabada

A full-stack software developer currently working on Ruby on Rails applications.