This skill should be used when the user asks about "views", "ERB templates", "partials", "layouts", "render", "form_with", "form helpers", "view helpers", "link_to", "content_for", "yield", "collection rendering", "locals", "tag helpers", "asset helpers", "image_tag", "stylesheet_link_tag", or needs guidance on building Rails views and templates.
/plugin marketplace add bastos/rails-plugin/plugin install bastos-ruby-on-rails@bastos/rails-pluginThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/form-patterns.mdreferences/view-helpers.mdComprehensive guide to Rails views, templates, partials, layouts, and view helpers.
<%# Comment - not rendered %>
<% code %> <%# Execute Ruby, no output %>
<%= expression %> <%# Output result (escaped) %>
<%== raw_html %> <%# Output without escaping (use carefully) %>
<%- code -%> <%# Suppress leading/trailing whitespace %>
<%# Conditionals %>
<% if @user.admin? %>
<span class="badge">Admin</span>
<% end %>
<%# Iteration %>
<% @articles.each do |article| %>
<article>
<h2><%= article.title %></h2>
<p><%= truncate(article.body, length: 100) %></p>
</article>
<% end %>
<%# Safe output %>
<%= sanitize(@article.body) %>
<%= raw(@trusted_html) %>
Partials are reusable view fragments. Filename starts with underscore.
<%# app/views/articles/_article.html.erb %>
<article id="<%= dom_id(article) %>">
<h2><%= article.title %></h2>
<p><%= article.body %></p>
</article>
<%# Render partial %>
<%= render "article", article: @article %>
<%= render partial: "article", locals: { article: @article } %>
<%# Renders _article.html.erb for each item %>
<%= render @articles %>
<%# Explicit collection %>
<%= render partial: "article", collection: @articles %>
<%# With spacer %>
<%= render partial: "article", collection: @articles, spacer_template: "article_divider" %>
<%# Cached collection %>
<%= render partial: "article", collection: @articles, cached: true %>
<%# Counter and iteration info available %>
<%# article_counter (0-indexed) %>
<%# article_iteration.first?, .last?, .index, .size %>
<%# Strict locals (Rails 7.1+) %>
<%# locals: (article:, show_author: true) -%>
<article>
<h2><%= article.title %></h2>
<% if show_author %>
<p>By <%= article.author.name %></p>
<% end %>
</article>
<%# Wrap partial in a layout %>
<%= render partial: "article", layout: "card", locals: { article: @article } %>
<%# app/views/shared/_card.html.erb %>
<div class="card">
<%= yield %>
</div>
<%# app/views/layouts/application.html.erb %>
<!DOCTYPE html>
<html>
<head>
<title><%= content_for(:title) || "My App" %></title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<%= yield :head %>
</head>
<body>
<%= render "shared/header" %>
<% if notice %>
<div class="notice"><%= notice %></div>
<% end %>
<% if alert %>
<div class="alert"><%= alert %></div>
<% end %>
<main>
<%= yield %>
</main>
<%= render "shared/footer" %>
</body>
</html>
<%# In view %>
<% content_for :title, "My Page Title" %>
<% content_for :head do %>
<%= stylesheet_link_tag "custom" %>
<% end %>
<% content_for :sidebar do %>
<nav>Sidebar content</nav>
<% end %>
<%# In layout %>
<%= content_for?(:sidebar) ? yield(:sidebar) : render("default_sidebar") %>
class AdminController < ApplicationController
layout "admin"
end
class ArticlesController < ApplicationController
layout "article", only: [:show]
layout :determine_layout
private
def determine_layout
current_user&.admin? ? "admin" : "application"
end
end
<%# Model-backed form %>
<%= form_with model: @article do |form| %>
<%= form.label :title %>
<%= form.text_field :title %>
<%= form.label :body %>
<%= form.text_area :body, rows: 10 %>
<%= form.label :status %>
<%= form.select :status, Article::STATUSES %>
<%= form.submit %>
<% end %>
<%# URL-based form %>
<%= form_with url: search_path, method: :get do |form| %>
<%= form.text_field :q, placeholder: "Search..." %>
<%= form.submit "Search" %>
<% end %>
<%# Text inputs %>
<%= form.text_field :name %>
<%= form.email_field :email %>
<%= form.password_field :password %>
<%= form.telephone_field :phone %>
<%= form.url_field :website %>
<%= form.number_field :quantity, min: 1, max: 100 %>
<%= form.range_field :rating, min: 1, max: 5 %>
<%= form.search_field :q %>
<%= form.text_area :description, rows: 5 %>
<%# Hidden %>
<%= form.hidden_field :status, value: "draft" %>
<%# Checkboxes and radios %>
<%= form.check_box :published %>
<%= form.radio_button :status, "draft" %>
<%= form.radio_button :status, "published" %>
<%# Select %>
<%= form.select :category_id, Category.pluck(:name, :id) %>
<%= form.select :role, %w[admin editor viewer], { include_blank: "Select role" } %>
<%# Collection select %>
<%= form.collection_select :category_id, Category.all, :id, :name %>
<%= form.collection_radio_buttons :status, Status.all, :id, :name %>
<%= form.collection_check_boxes :tag_ids, Tag.all, :id, :name %>
<%# Date/time %>
<%= form.date_field :published_on %>
<%= form.time_field :start_time %>
<%= form.datetime_local_field :event_at %>
<%= form.date_select :birthday %>
<%= form.time_zone_select :time_zone %>
<%# File upload %>
<%= form.file_field :avatar %>
<%= form.file_field :images, multiple: true %>
# Model
class Article < ApplicationRecord
has_many :comments
accepts_nested_attributes_for :comments, allow_destroy: true
end
<%= form_with model: @article do |form| %>
<%= form.text_field :title %>
<%= form.fields_for :comments do |comment_form| %>
<%= comment_form.text_area :body %>
<%= comment_form.check_box :_destroy %>
<%= comment_form.label :_destroy, "Delete" %>
<% end %>
<%= form.submit %>
<% end %>
<%# Links %>
<%= link_to "Home", root_path %>
<%= link_to "Article", @article %>
<%= link_to "Edit", edit_article_path(@article) %>
<%= link_to "Delete", @article, method: :delete, data: { turbo_method: :delete, turbo_confirm: "Sure?" } %>
<%# With block %>
<%= link_to article_path(@article) do %>
<strong><%= @article.title %></strong>
<% end %>
<%# Button (creates form) %>
<%= button_to "Subscribe", subscriptions_path, method: :post %>
<%# Email %>
<%= mail_to "support@example.com" %>
<%= mail_to "support@example.com", "Contact Us", subject: "Help Request" %>
<%# Current page check %>
<%= link_to "Home", root_path, class: ("active" if current_page?(root_path)) %>
<%# Modern tag helper syntax %>
<%= tag.div class: "container" do %>
<%= tag.h1 @article.title %>
<%= tag.p @article.body, class: "content" %>
<% end %>
<%# With data attributes %>
<%= tag.div data: { controller: "toggle", action: "click->toggle#switch" } do %>
Content
<% end %>
<%# Self-closing tags %>
<%= tag.br %>
<%= tag.hr %>
<%= tag.input type: "text", name: "query" %>
<%# class_names / token_list %>
<%= tag.div class: class_names("base", active: @active, hidden: @hidden) %>
<%# Images %>
<%= image_tag "logo.png" %>
<%= image_tag "logo.png", alt: "Logo", class: "logo", size: "100x50" %>
<%= image_tag @user.avatar, fallback: "default_avatar.png" %>
<%# Stylesheets %>
<%= stylesheet_link_tag "application", media: "all" %>
<%= stylesheet_link_tag "print", media: "print" %>
<%# JavaScript %>
<%= javascript_include_tag "application" %>
<%= javascript_importmap_tags %>
<%# Favicon %>
<%= favicon_link_tag "favicon.ico" %>
<%# Video/Audio %>
<%= video_tag "intro.mp4", controls: true, autoplay: false %>
<%= audio_tag "podcast.mp3", controls: true %>
<%# Truncate %>
<%= truncate(@article.body, length: 100) %>
<%= truncate(@article.body, length: 100, omission: "... (more)") %>
<%# Excerpt %>
<%= excerpt(@article.body, "Rails", radius: 25) %>
<%# Pluralize %>
<%= pluralize(@comments.count, "comment") %>
<%# Word wrap %>
<%= word_wrap(@text, line_width: 80) %>
<%# Simple format (converts newlines to <br> and paragraphs) %>
<%= simple_format(@article.body) %>
<%# Highlight %>
<%= highlight(@article.body, "Rails") %>
<%= number_to_currency(1234.56) %> <%# $1,234.56 %>
<%= number_to_currency(1234.56, unit: "€") %> <%# €1,234.56 %>
<%= number_to_percentage(75.5) %> <%# 75.500% %>
<%= number_to_percentage(75.5, precision: 0) %> <%# 76% %>
<%= number_with_delimiter(12345678) %> <%# 12,345,678 %>
<%= number_with_precision(111.2345, precision: 2) %> <%# 111.23 %>
<%= number_to_human(1234567) %> <%# 1.23 Million %>
<%= number_to_human_size(1234567) %> <%# 1.18 MB %>
<%= number_to_phone(5551234567) %> <%# 555-123-4567 %>
<%= distance_of_time_in_words(Time.current, 3.days.from_now) %> <%# about 3 days %>
<%= time_ago_in_words(@article.created_at) %> <%# 2 hours ago %>
<%# Remove dangerous HTML %>
<%= sanitize(@user_input) %>
<%# Allow specific tags %>
<%= sanitize(@content, tags: %w[p br strong em]) %>
<%# Strip all tags %>
<%= strip_tags(@html_content) %>
<%# Strip links but keep text %>
<%= strip_links(@content_with_links) %>
references/form-patterns.md - Complex form patterns, custom form buildersreferences/view-helpers.md - Creating custom helpers, helper organizationThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.