Back to skills
SkillHub ClubWrite Technical DocsFull StackFrontendTech Writer

rails-viewcomponents

Specialized skill for building ViewComponents with Hotwire (Turbo & Stimulus). Use when creating reusable UI components, implementing frontend interactions, building Turbo Frames/Streams, or writing Stimulus controllers. Includes component testing with Lookbook.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
44
Hot score
91
Updated
March 20, 2026
Overall rating
C3.3
Composite score
3.3
Best-practice grade
A88.4

Install command

npx @skill-hub/cli install alec-c4-claude-skills-rails-dev-rails-viewcomponents

Repository

alec-c4/claude-skills-rails-dev

Skill path: rails-viewcomponents

Specialized skill for building ViewComponents with Hotwire (Turbo & Stimulus). Use when creating reusable UI components, implementing frontend interactions, building Turbo Frames/Streams, or writing Stimulus controllers. Includes component testing with Lookbook.

Open repository

Best for

Primary workflow: Write Technical Docs.

Technical facets: Full Stack, Frontend, Tech Writer, Testing.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: alec-c4.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install rails-viewcomponents into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/alec-c4/claude-skills-rails-dev before adding rails-viewcomponents to shared team environments
  • Use rails-viewcomponents for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: rails-viewcomponents
description: Specialized skill for building ViewComponents with Hotwire (Turbo & Stimulus). Use when creating reusable UI components, implementing frontend interactions, building Turbo Frames/Streams, or writing Stimulus controllers. Includes component testing with Lookbook.
---

# Rails ViewComponents & Frontend

Build modern, component-based UIs with ViewComponent, Turbo, and Stimulus.

## When to Use This Skill

- Creating ViewComponents
- Building Turbo Frames and Streams
- Writing Stimulus controllers
- Implementing custom confirmation modals
- Creating Lookbook previews
- Building form interactions
- Real-time updates with Turbo Streams
- Progressive enhancement with Stimulus

## Core Principle: Component-Based Architecture

**ALL UI components MUST be ViewComponents** - not partials.

### Why ViewComponents?

- ✓ Better encapsulation than partials
- ✓ Testable in isolation
- ✓ Object-oriented approach
- ✓ Type safety and contracts
- ✓ Performance benefits (compiled)
- ✓ IDE support

## Critical ViewComponent Rules

**1. Prefix Rails helpers with `helpers.`**

```erb
<%# CORRECT %>
<%= helpers.link_to "Home", root_path %>
<%= helpers.image_tag "logo.png" %>
<%= helpers.inline_svg_tag "icons/user.svg" %>

<%# WRONG %>
<%= link_to "Home", root_path %>
```

**Exception**: `t()` i18n helper does NOT need prefix:

```erb
<%# CORRECT %>
<%= t('.title') %>
```

**2. SVG Icons as Separate Files**

Store SVGs in `app/assets/images/icons/` and render with [`inline_svg`](https://github.com/jamesmartin/inline_svg) gem:

```erb
<%= helpers.inline_svg_tag "icons/user.svg", class: "w-5 h-5" %>
```

**NEVER inline SVG markup in Ruby code**.

## Quick Component Patterns

### Basic Component

```ruby
# app/components/button_component.rb
class ButtonComponent < ViewComponent::Base
  def initialize(text:, variant: :primary, **options)
    @text = text
    @variant = variant
    @options = options
  end
end
```

```erb
<%# app/components/button_component.html.erb %>
<button class="btn btn-<%= @variant %>" <%= html_attributes(@options) %>>
  <%= @text %>
</button>
```

### Component with Slots

```ruby
class CardComponent < ViewComponent::Base
  renders_one :header
  renders_one :footer
  renders_many :actions
end
```

```erb
<%= render CardComponent.new do |card| %>
  <% card.with_header do %>
    <h3>Title</h3>
  <% end %>

  <p>Body content</p>

  <% card.with_action do %>
    <%= helpers.link_to "Edit", edit_path %>
  <% end %>
<% end %>
```

### Component with Variants

```ruby
class BadgeComponent < ViewComponent::Base
  VARIANTS = {
    primary: "bg-blue-100 text-blue-800",
    success: "bg-green-100 text-green-800",
    danger: "bg-red-100 text-red-800"
  }.freeze

  def initialize(text:, variant: :primary)
    @text = text
    @variant = variant
  end

  def variant_classes
    VARIANTS[@variant]
  end
end
```

## Turbo Frames & Streams

### Turbo Frame (Edit in Place)

```erb
<%# index.html.erb %>
<%= turbo_frame_tag dom_id(article) do %>
  <h2><%= article.title %></h2>
  <%= link_to "Edit", edit_article_path(article) %>
<% end %>
```

```erb
<%# edit.html.erb %>
<%= turbo_frame_tag dom_id(@article) do %>
  <%= form_with model: @article do |f| %>
    <%= f.text_field :title %>
    <%= f.submit %>
  <% end %>
<% end %>
```

### Turbo Streams (Multiple Updates)

```ruby
# Controller
def create
  @article = Article.new(article_params)

  respond_to do |format|
    if @article.save
      format.turbo_stream do
        render turbo_stream: [
          turbo_stream.prepend("articles", partial: "article", locals: { article: @article }),
          turbo_stream.update("form", partial: "form", locals: { article: Article.new })
        ]
      end
    end
  end
end
```

## Stimulus Controllers

### Basic Controller

```javascript
// app/javascript/controllers/dropdown_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["menu"]
  static values = { open: Boolean }

  toggle() {
    this.openValue = !this.openValue
  }

  openValueChanged() {
    if (this.openValue) {
      this.menuTarget.classList.remove("hidden")
    } else {
      this.menuTarget.classList.add("hidden")
    }
  }
}
```

```erb
<div data-controller="dropdown">
  <button data-action="dropdown#toggle">Options</button>
  <div data-dropdown-target="menu" class="hidden">
    <a href="#">Edit</a>
  </div>
</div>
```

## Custom Confirmation Modals

**ALWAYS use custom modals instead of browser `confirm()`**

```javascript
// app/javascript/controllers/confirmation_modal_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["modal", "title", "message", "confirmButton"]

  show(options = {}) {
    this.titleTarget.textContent = options.title || "Confirm"
    this.messageTarget.textContent = options.message || "Are you sure?"
    this.modalTarget.classList.remove("hidden")
    this.onConfirm = options.onConfirm || (() => {})
  }

  confirm() {
    this.onConfirm()
    this.hide()
  }

  hide() {
    this.modalTarget.classList.add("hidden")
  }
}
```

## Lookbook Previews

Required for shared components:

```ruby
# spec/components/previews/button_component_preview.rb
class ButtonComponentPreview < ViewComponent::Preview
  def default
    render ButtonComponent.new(text: "Click me")
  end

  def primary
    render ButtonComponent.new(text: "Primary", variant: :primary)
  end

  def danger
    render ButtonComponent.new(text: "Delete", variant: :danger)
  end
end
```

Access at: `http://localhost:3000/lookbook`

## Testing Components

```ruby
RSpec.describe ButtonComponent, type: :component do
  it "renders button text" do
    render_inline(ButtonComponent.new(text: "Click me"))
    expect(page).to have_button("Click me")
  end

  it "applies variant classes" do
    render_inline(ButtonComponent.new(text: "Save", variant: :primary))
    expect(page).to have_css("button.btn-primary")
  end
end
```

## Tech Stack

- **ViewComponent** - Component framework
- **Lookbook** - Component documentation
- **Turbo** - SPA-like interactions
- **Stimulus** - JavaScript controllers
- **Tailwind CSS** - Styling (typical)
- **[inline_svg](https://github.com/jamesmartin/inline_svg)** - SVG rendering gem

## Reference Documentation

For comprehensive frontend patterns:
- Frontend guide: `frontend.md` (detailed examples and advanced patterns)
rails-viewcomponents | SkillHub