Views & ERB
Rails views are ERB / HAML templates that render the response. Mastering helpers, partials, layouts, and content_for keeps the markup DRY and the controllers thin. Modern Rails pairs the view with Turbo Frames and Stimulus for SPA-feel without leaving the framework.
ERB templates, partials, layouts, Turbo Frames
EXAMPLE
# 1) Layout — wraps every view rendered by a controller
# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<title><%= content_for(:title) || 'Shop' %></title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', 'data-turbo-track': 'reload' %>
<%= javascript_importmap_tags %>
</head>
<body>
<%= render 'shared/nav' %>
<%= yield :flash %>
<main class='container'>
<%= yield %>
</main>
<%= render 'shared/footer' %>
</body>
</html>
# 2) Action view — sets the title for the layout
# app/views/orders/index.html.erb
<% content_for :title, 'Orders' %>
<h1>Orders</h1>
# 3) Partials — _underscore prefix, rendered with render
# app/views/orders/_order_row.html.erb
<tr id='<%= dom_id(order) %>'>
<td><%= link_to order.id, order %></td>
<td><%= order.customer.name %></td>
<td><%= number_to_currency(order.total_aud) %></td>
<td><%= order.status %></td>
</tr>
# Render a single partial
# app/views/orders/show.html.erb
<%= render 'order_row', order: @order %>
# Render a COLLECTION (convention: views/orders/_order.html.erb)
<%= render @orders %>
# Equivalent to:
# <% @orders.each do |o| %><%= render 'order', order: o %><% end %>
# 4) Helpers — define in app/helpers/orders_helper.rb
module OrdersHelper
def order_status_badge(order)
color = { 'new' => 'gray', 'paid' => 'green', 'shipped' => 'blue', 'cancelled' => 'red' }[order.status]
content_tag(:span, order.status, class: "badge badge-#{color}")
end
end
# Use in any view that the OrdersController renders
# <%= order_status_badge(@order) %>
# 5) Form helpers (form_with) — Rails 7+ uses Turbo by default
# app/views/orders/_form.html.erb
<%= form_with model: order, local: false do |f| %>
<% if order.errors.any? %>
<div class='alert'>
<%= pluralize(order.errors.count, 'error') %> stopped the save:
<ul>
<% order.errors.full_messages.each do |m| %>
<li><%= m %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :customer_id %>
<%= f.collection_select :customer_id, Customer.all, :id, :name %>
<%= f.label :total_cents %>
<%= f.number_field :total_cents, min: 0 %>
<%= f.submit %>
<% end %>
# 6) Turbo Frames — replace a region without a full page reload
# app/views/orders/_card.html.erb
<%= turbo_frame_tag dom_id(order) do %>
<h3><%= order.customer.name %></h3>
<%= link_to 'Edit', edit_order_path(order) %>
<% end %>
# When you click the Edit link, Rails fetches the response and replaces
# the frame contents in place. Set the responding controller to render
# the same turbo_frame_tag id.
# 7) Turbo Streams — broadcast list mutations
# app/views/orders/create.turbo_stream.erb
<%= turbo_stream.prepend 'orders' do %>
<%= render @order %>
<% end %>
# 8) Content blocks and yield
# Layout has <%= yield :sidebar %>
# Inside a view:
# <% content_for :sidebar do %>
# <%= render 'orders/sidebar' %>
# <% end %>
# 9) Caching expensive partials
# <% cache @order do %>
# <%= render 'order_card', order: @order %>
# <% end %>
# Russian-doll caching: a parent cache_key changes when a child does.
# 10) Common pitfalls
# - Business logic in the view (move to model / helper / decorator)
# - N+1 queries — use includes() in the controller; verify with the bullet gem
# - Forgetting Turbo and writing tons of JS
# - Not escaping output — Rails default IS safe, but raw / html_safe disable it
# - Long ERB files; break into partials at the first sign of nesting
Why it matters
Render collections with `render @orders` — it auto-picks the partial named `orders/_order.html.erb` and is heavily optimised. The convention makes view code shorter, prevents per-row partial duplication, and unlocks Russian-doll caching with zero extra config.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
<%# app/views/posts/show.html.erb %> <h1><%= @post.title %></h1> <p><%= @post.body %></p>Try it Yourself »
Discussion
Loading…