Rails 6.1 introduces class_names helper

Rails 6.1 introduced the class_names helper to make it easier to add conditional class names to HTML elements. This helper simplifies the common pattern of conditionally applying CSS classes.

Before Rails 6.1

Previously, you had to manually concatenate class names:

<!-- Manual concatenation -->
<div class="<%= 'active' if user.active? %> <%= 'admin' if user.admin? %>">
  User content
</div>

<!-- Or using array join -->
<div class="<%= ['active' if user.active?, 'admin' if user.admin?].compact.join(' ') %>">
  User content
</div>

This approach was verbose and error-prone.

Rails 6.1 Solution

Rails 6.1 provides the class_names helper:

<div class="<%= class_names('base-class', active: user.active?, admin: user.admin?) %>">
  User content
</div>

Basic Usage

Simple Classes

Add classes unconditionally:

<%= class_names('btn', 'btn-primary') %>
<!-- Output: "btn btn-primary" -->

Conditional Classes

Add classes conditionally:

<%= class_names('btn', active: user.active?) %>
<!-- Output: "btn active" if user.active? is true -->
<!-- Output: "btn" if user.active? is false -->

Multiple Conditional Classes

Combine multiple conditions:

<%= class_names(
  'btn',
  'btn-primary': user.primary?,
  'btn-large': user.large?,
  'disabled': user.disabled?
) %>

Hash Syntax

Use hash syntax for conditional classes:

<!-- Class name as key, condition as value -->
<%= class_names(
  'card',
  'active' => post.published?,
  'featured' => post.featured?,
  'archived' => post.archived?
) %>

Array Syntax

You can also use arrays:

<%= class_names(['btn', 'btn-primary'], 'btn-large': large?) %>

Real-World Examples

Navigation Links

<%= link_to 'Home', root_path, class: class_names(
  'nav-link',
  'active' => current_page?(root_path)
) %>

Form Fields

<%= form.text_field :email, class: class_names(
  'form-control',
  'is-invalid' => @user.errors[:email].any?,
  'is-valid' => @user.errors[:email].empty? && @user.persisted?
) %>

Cards

<div class="<%= class_names(
  'card',
  'card-featured' => post.featured?,
  'card-published' => post.published?,
  "card-#{post.status}"
) %>">
  <!-- Card content -->
</div>

Combining with Other Helpers

With content_tag

content_tag :div, class: class_names('container', fluid: fluid?) do
  # Content
end

With link_to

<%= link_to 'Profile', user_path(user), class: class_names(
  'btn',
  'btn-primary' => current_user == user,
  'btn-secondary' => current_user != user
) %>

Advanced Usage

Dynamic Class Names

Combine static and dynamic classes:

<%= class_names(
  'user',
  "user-#{user.role}",
  "user-#{user.status}",
  'online' => user.online?,
  'offline' => !user.online?
) %>

Nested Conditions

Handle complex conditions:

<%= class_names(
  'notification',
  'unread' => notification.unread?,
  'important' => notification.important? && notification.unread?,
  'archived' => notification.archived?
) %>

Comparison with Alternatives

Using String Interpolation

<!-- Before: Error-prone -->
<div class="btn <%= 'active' if active? %> <%= 'disabled' if disabled? %>">

<!-- After: Clean and safe -->
<div class="<%= class_names('btn', active: active?, disabled: disabled?) %>">

Using Array Join

<!-- Before: Verbose -->
<div class="<%= ['btn', ('active' if active?), ('disabled' if disabled?)].compact.join(' ') %>">

<!-- After: Concise -->
<div class="<%= class_names('btn', active: active?, disabled: disabled?) %>">

Benefits

The class_names helper provides several advantages:

  • Cleaner code: More readable than manual concatenation
  • Null safety: Handles nil values gracefully
  • Flexibility: Supports multiple syntaxes
  • Consistency: Standard way to handle conditional classes

Edge Cases

Nil Values

class_names handles nil gracefully:

class_names('btn', active: nil)  # => "btn"
class_names('btn', active: false) # => "btn"
class_names('btn', active: true)  # => "btn active"

Empty Strings

Empty strings are ignored:

class_names('btn', '')  # => "btn"
class_names('', 'btn')  # => "btn"

Best Practices

  1. Use for conditional classes: Especially useful when you have multiple conditions
  2. Keep it readable: Don't nest too many conditions
  3. Combine with helpers: Works well with Rails view helpers
  4. Use hash syntax: Prefer hash syntax for clarity

Conclusion

Rails 6.1's class_names helper simplifies adding conditional CSS classes to HTML elements. It provides a clean, readable way to handle complex class name logic, making your views more maintainable and less error-prone.

Rails 6.1 introduces class_names helper - Abhay Nikam