rails-views

Output <%= @post.title %>

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "rails-views" with this command: npx skills add shoebtamboli/rails_claude_skills/shoebtamboli-rails-claude-skills-rails-views

Rails Views

Quick Reference

Pattern Example

Output <%= @post.title %>

Code `<% @posts.each do

Link <%= link_to 'Home', root_path %>

Form `<%= form_with model: @post do

Partial <%= render 'shared/header' %>

Helper <%= truncate @post.body, length: 100 %>

Asset <%= image_tag 'logo.png' %>

ERB Basics

<%# Comment - won't be rendered %>

<% # Ruby code - executed but not displayed %> <% if user_signed_in? %> <p>Welcome back!</p> <% end %>

<%= # Ruby code with output %> <%= @post.title %> <%= current_user.name %>

<%== # Output without HTML escaping (dangerous!) %> <%== raw_html_content %>

<%- # Suppress whitespace before tag %> <%- if condition -%>

<%= # Safe output (escapes HTML by default) %> <%= user_input %> <%# Safe from XSS %>

Layouts

<%# app/views/layouts/application.html.erb %> <!DOCTYPE html> <html> <head> <title><%= content_for?(:title) ? yield(:title) : "My App" %></title> <%= csrf_meta_tags %> <%= csp_meta_tag %>

&#x3C;%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
&#x3C;%= javascript_importmap_tags %>

&#x3C;%= yield :head %>

</head>

<body class="<%= controller_name %> <%= action_name %>"> <%= render 'shared/header' %>

&#x3C;% flash.each do |type, message| %>
  &#x3C;div class="alert alert-&#x3C;%= type %>">
    &#x3C;%= message %>
  &#x3C;/div>
&#x3C;% end %>

&#x3C;main>
  &#x3C;%= yield %>
&#x3C;/main>

&#x3C;%= render 'shared/footer' %>

</body> </html>

Content For

<%# In view template %> <% content_for :title do %> <%= @post.title %> - My Blog <% end %>

<% content_for :head do %> <%= stylesheet_link_tag "posts" %> <meta name="description" content="<%= @post.excerpt %>"> <% end %>

<%# Content here will be yielded in layout %> <article> <%= @post.body %> </article>

Partials

Basic Partials

<%# Render partial %> <%= render 'shared/header' %> <%= render 'post' %> <%= render partial: 'post' %>

<%# With local variables %> <%= render 'post', post: @post %> <%= render partial: 'post', locals: { post: @post, show_author: true } %>

<%# Partial file: app/views/shared/_header.html.erb %> <header> <h1>My Blog</h1> </header>

<%# Partial file: app/views/posts/_post.html.erb %> <article> <h2><%= post.title %></h2> <p><%= post.body %></p> <% if local_assigns[:show_author] %> <p>By <%= post.author.name %></p> <% end %> </article>

Collection Partials

<%# Render for each item %> <%= render partial: 'post', collection: @posts %>

<%# Shorthand %> <%= render @posts %>

<%# With local variable name %> <%= render partial: 'post', collection: @posts, as: :item %>

<%# With spacer template %> <%= render partial: 'post', collection: @posts, spacer_template: 'post_divider' %>

<%# In partial, access current index %> <article data-index="<%= post_counter %>"> <%= post.title %> </article>

Forms

Form Helpers

<%= form_with model: @post do |f| %> <% if @post.errors.any? %> <div class="errors"> <h3><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h3> <ul> <% @post.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %>

<div class="field"> <%= f.label :title %> <%= f.text_field :title, class: 'form-control' %> </div>

<div class="field"> <%= f.label :body %> <%= f.text_area :body, rows: 10, class: 'form-control' %> </div>

<div class="field"> <%= f.label :published %> <%= f.check_box :published %> </div>

<div class="field"> <%= f.label :category_id %> <%= f.collection_select :category_id, Category.all, :id, :name, { prompt: 'Select a category' }, { class: 'form-control' } %> </div>

<div class="field"> <%= f.label :tag_ids %> <%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name %> </div>

<div class="actions"> <%= f.submit "Save Post", class: 'btn btn-primary' %> </div> <% end %>

Form Field Types

<%= f.text_field :name %> <%= f.text_area :description %> <%= f.password_field :password %> <%= f.email_field :email %> <%= f.url_field :website %> <%= f.number_field :age %> <%= f.date_field :birthday %> <%= f.datetime_field :published_at %> <%= f.time_field :starts_at %> <%= f.hidden_field :user_id %>

<%= f.check_box :published %> <%= f.radio_button :status, 'active' %>

<%= f.select :category_id, Category.pluck(:name, :id) %> <%= f.collection_select :author_id, User.all, :id, :name %> <%= f.collection_radio_buttons :status, Status.all, :id, :name %> <%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name %>

<%= f.file_field :avatar %>

Non-Model Forms

<%= form_with url: search_path, method: :get do |f| %> <%= f.text_field :query, placeholder: 'Search...' %> <%= f.submit 'Search' %> <% end %>

Links and URLs

<%# Basic link %> <%= link_to 'Home', root_path %> <%= link_to 'Edit', edit_post_path(@post) %> <%= link_to 'Delete', post_path(@post), data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } %>

<%# Link to object (uses polymorphic routing) %> <%= link_to @post.title, @post %> <%= link_to 'Edit', [:edit, @post] %>

<%# Link with block %> <%= link_to post_path(@post) do %> <strong><%= @post.title %></strong> <p><%= @post.excerpt %></p> <% end %>

<%# Link classes and data attributes %> <%= link_to 'Click', path, class: 'btn btn-primary', data: { action: 'click->controller#method' } %>

<%# Button to (generates a form) %> <%= button_to 'Delete', post_path(@post), method: :delete, class: 'btn btn-danger' %>

<%# Mail to %> <%= mail_to 'user@example.com' %> <%= mail_to 'user@example.com', 'Contact Us', subject: 'Hello' %>

Asset Helpers

<%# Images %> <%= image_tag 'logo.png' %> <%= image_tag 'logo.png', alt: 'Logo', class: 'logo', size: '100x100' %> <%= image_tag @post.cover_image_url %>

<%# Stylesheets %> <%= stylesheet_link_tag 'application' %> <%= stylesheet_link_tag 'posts', media: 'all' %>

<%# JavaScript %> <%= javascript_include_tag 'application' %> <%= javascript_importmap_tags %>

<%# Asset path %> <%= asset_path 'image.png' %> <%= asset_url 'image.png' %>

View Helpers

Text Helpers

<%= truncate @post.body, length: 100 %> <%= truncate @post.body, length: 100, separator: ' ' %>

<%= simple_format @post.body %>

<%= pluralize @posts.count, 'post' %>

<%= number_to_currency 29.99 %> <%= number_to_percentage 85.5 %> <%= number_with_delimiter 1000000 %> <%= number_to_human 1234567 %>

<%= time_ago_in_words @post.created_at %> <%= distance_of_time_in_words Time.now, @post.created_at %>

Content Helpers

<%= content_tag :div, "Hello", class: 'greeting' %> <%# Output: <div class="greeting">Hello</div> %>

<%= content_tag :div, class: 'post' do %> <%= @post.title %> <% end %>

<%= tag.div "Hello", class: 'greeting' %> <%= tag.div class: 'post' do %> <%= @post.title %> <% end %>

Sanitization

<%# Strip all HTML tags %> <%= strip_tags @post.html_content %>

<%# Allow specific tags %> <%= sanitize @post.html_content, tags: %w[p br strong em] %>

<%# Escape HTML %> <%= html_escape user_input %> <%= h user_input %> <%# shorthand %>

Custom Helpers

app/helpers/application_helper.rb

module ApplicationHelper def page_title(title) content_for(:title) { title } content_tag(:h1, title, class: 'page-title') end

def active_link(text, path, **options) active = current_page?(path) classes = options[:class].to_s classes += ' active' if active link_to text, path, class: classes end

def formatted_date(date) return 'N/A' unless date date.strftime('%B %d, %Y') end

def user_avatar(user, size: 50) if user.avatar.attached? image_tag user.avatar.variant(resize_to_limit: [size, size]) else image_tag "default-avatar.png", size: "#{size}x#{size}" end end end

<%# Using custom helpers %> <%= page_title "My Posts" %> <%= active_link "Home", root_path, class: 'nav-link' %> <%= formatted_date @post.created_at %> <%= user_avatar current_user, size: 100 %>

Turbo Frames

<%# Turbo Frame %> <%= turbo_frame_tag "post_#{@post.id}" do %> <%= render @post %> <% end %>

<%# Turbo Frame with lazy loading %> <%= turbo_frame_tag "post_#{@post.id}", src: post_path(@post), loading: :lazy do %> Loading... <% end %>

<%# Target a specific frame %> <%= link_to "Edit", edit_post_path(@post), data: { turbo_frame: "post_#{@post.id}" } %>

Turbo Streams

<%# app/views/posts/create.turbo_stream.erb %> <%= turbo_stream.prepend "posts" do %> <%= render @post %> <% end %>

<%= turbo_stream.update "flash" do %> <div class="notice">Post created!</div> <% end %>

<%# Available actions: append, prepend, replace, update, remove, before, after %>

Conditional Rendering

<% if user_signed_in? %> <p>Welcome, <%= current_user.name %>!</p> <%= link_to "Logout", logout_path, data: { turbo_method: :delete } %> <% else %> <%= link_to "Login", login_path %> <% end %>

<% unless @posts.empty? %> <%= render @posts %> <% else %> <p>No posts yet.</p> <% end %>

<%# Ternary operator %> <%= @post.published? ? "Published" : "Draft" %>

Loops and Iteration

<% @posts.each do |post| %> <%= render post %> <% end %>

<% @posts.each_with_index do |post, index| %> <div class="post-<%= index + 1 %>"> <%= render post %> </div> <% end %>

<%# Check if collection is empty %> <% if @posts.any? %> <% @posts.each do |post| %> <%= render post %> <% end %> <% else %> <p>No posts found.</p> <% end %>

Best Practices

  • Keep views simple - Complex logic belongs in helpers or models

  • Use partials for reusable components

  • Use helpers for view-specific logic

  • Escape user input (Rails does this by default with <%= %> )

  • Use semantic HTML for accessibility

  • Leverage Turbo for reactive UIs without JavaScript

  • Use content_for for flexible layouts

  • Keep CSS/JS out of ERB files (use asset pipeline)

  • Use I18n for text content to support internationalization

  • Test helpers with unit tests

Common Patterns

Conditional Class Names

<div class="<%= 'active' if @post.published? %> post"> ... </div>

<%# Better with helper %> <div class="<%= post_classes(@post) %>"> ... </div>

In helper

def post_classes(post) classes = ['post'] classes << 'published' if post.published? classes << 'featured' if post.featured? classes.join(' ') end

Empty State

<% if @posts.any? %> <%= render @posts %> <% else %> <div class="empty-state"> <p>No posts yet.</p> <%= link_to "Create your first post", new_post_path, class: 'btn' %> </div> <% end %>

References

  • Rails Guides - Layouts and Rendering

  • Rails Guides - Form Helpers

  • Rails Guides - Action View

  • Hotwire Turbo Handbook

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

rails-debugging

No summary provided by upstream source.

Repository SourceNeeds Review
General

rails-controllers

No summary provided by upstream source.

Repository SourceNeeds Review
General

rails-authorization-cancancan

No summary provided by upstream source.

Repository SourceNeeds Review
General

rails-pagination-kaminari

No summary provided by upstream source.

Repository SourceNeeds Review