
viewcomponent-coder
by majesticlabs-dev
SKILL.md
name: viewcomponent-coder description: Build component-based UIs with ViewComponent and view_component-contrib. Use when creating reusable UI components, implementing slots and style variants, or building component previews. Triggers on ViewComponent creation, component patterns, Lookbook previews, or UI component architecture.
ViewComponent Patterns
Build modern, component-based UIs with ViewComponent using Evil Martians' view_component-contrib patterns.
When to Use This Skill
- Creating ViewComponent classes
- Implementing slots and style variants
- Building Lookbook previews
- Testing components in isolation
- Refactoring partials to components
Core Principle: Components Over Partials
Prefer ViewComponents over partials for reusable UI.
Why ViewComponents?
- Better encapsulation than partials
- Testable in isolation
- Object-oriented approach with explicit contracts
- IDE support and type safety
- Performance benefits (compiled templates)
Setup
# Gemfile
gem "view_component"
gem "view_component-contrib" # Evil Martians patterns
gem "dry-initializer" # Declarative initialization
gem "lookbook" # Component previews
gem "inline_svg" # SVG icons
Install with Rails template:
rails app:template LOCATION="https://railsbytes.com/script/zJosO5"
Base Classes
# app/components/application_view_component.rb
class ApplicationViewComponent < ViewComponentContrib::Base
extend Dry::Initializer
end
# spec/components/previews/application_view_component_preview.rb
class ApplicationViewComponentPreview < ViewComponentContrib::Preview::Base
self.abstract_class = true
end
Basic Component with dry-initializer
# app/components/button_component.rb
class ButtonComponent < ApplicationViewComponent
option :text
option :variant, default: -> { :primary }
option :size, default: -> { :md }
end
<%# app/components/button_component.html.erb %>
<button class="btn btn-<%= variant %> btn-<%= size %>">
<%= text %>
</button>
Style Variants DSL
Replace manual VARIANTS hashes with the Style Variants DSL:
class ButtonComponent < ApplicationViewComponent
include ViewComponentContrib::StyleVariants
option :text
option :color, default: -> { :primary }
option :size, default: -> { :md }
style do
base { %w[font-medium rounded-full] }
variants {
color {
primary { %w[bg-blue-500 text-white] }
secondary { %w[bg-gray-500 text-white] }
danger { %w[bg-red-500 text-white] }
}
size {
sm { "text-sm px-2 py-1" }
md { "text-base px-4 py-2" }
lg { "text-lg px-6 py-3" }
}
}
# Apply when multiple conditions match
compound(size: :lg, color: :primary) { "uppercase" }
defaults { { color: :primary, size: :md } }
end
end
<button class="<%= style(color:, size:) %>">
<%= text %>
</button>
Component with Slots
class CardComponent < ApplicationViewComponent
renders_one :header
renders_one :footer
renders_many :actions
end
<%= 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 %>
Important Rules
1. Prefix Rails helpers with helpers.
<%# CORRECT %>
<%= helpers.link_to "Home", root_path %>
<%= helpers.image_tag "logo.png" %>
<%= helpers.inline_svg_tag "icons/user.svg" %>
<%# WRONG - will fail in component context %>
<%= link_to "Home", root_path %>
Exception: t() i18n helper does NOT need prefix:
<%= t('.title') %>
2. SVG Icons as Separate Files
Store SVGs in app/assets/images/icons/ and render with inline_svg gem:
<%= helpers.inline_svg_tag "icons/user.svg", class: "w-5 h-5" %>
Don't inline SVG markup in Ruby code - use separate files instead.
Conditional Rendering
class AlertComponent < ApplicationViewComponent
option :message
option :type, default: -> { :info }
option :dismissible, default: -> { true }
# Skip rendering if no message
def render?
message.present?
end
end
Lookbook Previews
# spec/components/previews/button_component_preview.rb
class ButtonComponentPreview < ApplicationViewComponentPreview
def default
render ButtonComponent.new(text: "Click me")
end
def primary
render ButtonComponent.new(text: "Primary", color: :primary)
end
def all_sizes
render_with(wrapper: :flex_row) do
safe_join([
render(ButtonComponent.new(text: "Small", size: :sm)),
render(ButtonComponent.new(text: "Medium", size: :md)),
render(ButtonComponent.new(text: "Large", size: :lg))
])
end
end
end
Access at: http://localhost:3000/lookbook
Testing Components
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 style variant classes" do
render_inline(ButtonComponent.new(text: "Save", color: :primary, size: :lg))
expect(page).to have_css("button.bg-blue-500.text-lg")
end
end
Detailed References
For advanced patterns and examples:
references/patterns.md- Slots, collections, polymorphic components, Turbo integrationreferences/style-variants.md- Full Style Variants DSL, compound variants, TailwindMerge
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon