From majestic-rails
Builds reusable UI components with ViewComponent and view_component-contrib, including slots, style variants, Lookbook previews, and refactoring partials to components.
How this skill is triggered — by the user, by Claude, or both
Slash command
/majestic-rails:viewcomponent-coderThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Build modern, component-based UIs with ViewComponent using Evil Martians' [view_component-contrib](https://github.com/palkan/view_component-contrib) patterns.
Build modern, component-based UIs with ViewComponent using Evil Martians' view_component-contrib patterns.
Prefer ViewComponents over partials for reusable UI.
# 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"
# 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
# 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>
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>
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 %>
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.
class AlertComponent < ApplicationViewComponent
option :message
option :type, default: -> { :info }
option :dismissible, default: -> { true }
# Skip rendering if no message
def render?
message.present?
end
end
# 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
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
For advanced patterns and examples:
references/patterns.md - Slots, collections, polymorphic components, Turbo integrationreferences/style-variants.md - Full Style Variants DSL, compound variants, TailwindMergenpx claudepluginhub majesticlabs-dev/majestic-marketplace --plugin majestic-railsStyles Rails views using Tailwind CSS utilities and DaisyUI components for responsive, themeable, accessible UIs without custom CSS.
Applies Tailwind CSS utility classes and responsive patterns to Rails ERB views, integrating with helpers like link_to, form_with, and flash messages.
Generates copy-pasteable, responsive UI components like navbars, cards, modals, sidebars, and pricing pages. Adapts to project's React, Vue, Svelte, Tailwind, Bootstrap, or HTML/CSS stack.