Partials and Layouts
Partials and layouts help you organize your views and keep them DRY. Layouts provide the common structure, while partials are reusable view fragments.
Layouts
Layouts wrap your views with common HTML structure:
erb
1<!-- app/views/layouts/application.html.erb -->
2<!DOCTYPE html>
3<html>
4<head>
5 <title><%= content_for(:title) || "My App" %></title>
6 <%= csrf_meta_tags %>
7 <%= csp_meta_tag %>
8 <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
9 <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
10 <%= yield :head %>
11</head>
12<body class="<%= controller_name %> <%= action_name %>">
13 <%= render "shared/navigation" %>
14
15 <main class="container">
16 <%= render "shared/flash_messages" %>
17 <%= yield %>
18 </main>
19
20 <%= render "shared/footer" %>
21</body>
22</html>Multiple Layouts
ruby
1class AdminController < ApplicationController
2 layout "admin"
3end
4
5class ArticlesController < ApplicationController
6 layout "blog", only: [:index, :show]
7
8 # Dynamic layout
9 layout :choose_layout
10
11 private
12
13 def choose_layout
14 current_user&.admin? ? "admin" : "application"
15 end
16endDisabling Layout
ruby
1class ApiController < ApplicationController
2 layout false
3end
4
5# Or per action
6def show
7 render layout: false
8endContent For
Pass content from views to layouts:
erb
1<!-- app/views/articles/show.html.erb -->
2<% content_for :title do %>
3 <%= @article.title %> - My Blog
4<% end %>
5
6<% content_for :head do %>
7 <meta name="description" content="<%= @article.excerpt %>">
8<% end %>
9
10<% content_for :sidebar do %>
11 <%= render "related_articles" %>
12<% end %>
13
14<article>
15 <h1><%= @article.title %></h1>
16 <%= @article.body %>
17</article>erb
1<!-- In layout -->
2<head>
3 <title><%= yield :title %></title>
4 <%= yield :head %>
5</head>
6<body>
7 <main><%= yield %></main>
8 <aside><%= yield :sidebar %></aside>
9</body>Check if Content Exists
erb
1<% if content_for?(:sidebar) %>
2 <aside><%= yield :sidebar %></aside>
3<% end %>Partials
Partials are reusable view fragments. Their filenames start with an underscore:
erb
1<!-- app/views/articles/_article.html.erb -->
2<article class="article-card">
3 <h2><%= link_to article.title, article %></h2>
4 <p><%= truncate(article.body, length: 150) %></p>
5 <footer>
6 By <%= article.user.name %> •
7 <%= time_ago_in_words(article.created_at) %> ago
8 </footer>
9</article>Rendering Partials
erb
1<!-- Basic render -->
2<%= render "article" %>
3<!-- Looks for _article.html.erb in current view folder -->
4
5<!-- With explicit path -->
6<%= render "shared/header" %>
7<%= render "articles/article" %>
8
9<!-- With local variables -->
10<%= render "article", article: @article %>
11<%= render partial: "article", locals: { article: @article } %>Rendering Collections
erb
1<!-- Long form -->
2<% @articles.each do |article| %>
3 <%= render "article", article: article %>
4<% end %>
5
6<!-- Short form (Rails magic) -->
7<%= render @articles %>
8<!-- Renders _article.html.erb for each article -->
9
10<!-- Explicit collection -->
11<%= render partial: "article", collection: @articles %>
12
13<!-- Custom variable name -->
14<%= render partial: "article", collection: @articles, as: :post %>
15
16<!-- With spacer template -->
17<%= render partial: "article", collection: @articles, spacer_template: "separator" %>
18
19<!-- Empty state -->
20<%= render @articles || render("empty_state") %>Passing Objects
erb
1<!-- When partial name matches model -->
2<%= render @article %>
3<!-- Renders app/views/articles/_article.html.erb with local variable 'article' -->
4
5<!-- This is equivalent to -->
6<%= render partial: "articles/article", locals: { article: @article } %>Common Partial Patterns
Form Partial
erb
1<!-- app/views/articles/_form.html.erb -->
2<%= form_with model: article do |f| %>
3 <% if article.errors.any? %>
4 <div class="errors">
5 <% article.errors.full_messages.each do |msg| %>
6 <p><%= msg %></p>
7 <% end %>
8 </div>
9 <% end %>
10
11 <div class="field">
12 <%= f.label :title %>
13 <%= f.text_field :title %>
14 </div>
15
16 <div class="field">
17 <%= f.label :body %>
18 <%= f.text_area :body %>
19 </div>
20
21 <%= f.submit %>
22<% end %>erb
1<!-- new.html.erb -->
2<h1>New Article</h1>
3<%= render "form", article: @article %>
4
5<!-- edit.html.erb -->
6<h1>Edit Article</h1>
7<%= render "form", article: @article %>Navigation Partial
erb
1<!-- app/views/shared/_navigation.html.erb -->
2<nav class="navbar">
3 <%= link_to "Home", root_path, class: nav_link_class(root_path) %>
4 <%= link_to "Articles", articles_path, class: nav_link_class(articles_path) %>
5
6 <% if user_signed_in? %>
7 <span>Welcome, <%= current_user.name %></span>
8 <%= button_to "Log Out", logout_path, method: :delete %>
9 <% else %>
10 <%= link_to "Log In", login_path %>
11 <% end %>
12</nav>Flash Messages Partial
erb
1<!-- app/views/shared/_flash_messages.html.erb -->
2<% flash.each do |type, message| %>
3 <div class="alert alert-<%= type %>" data-controller="dismissable">
4 <%= message %>
5 <button data-action="click->dismissable#dismiss">×</button>
6 </div>
7<% end %>Card Component Partial
erb
1<!-- app/views/shared/_card.html.erb -->
2<div class="card">
3 <% if local_assigns[:image] %>
4 <img src="<%= image %>" class="card-img">
5 <% end %>
6
7 <div class="card-body">
8 <h3 class="card-title"><%= title %></h3>
9 <% if local_assigns[:subtitle] %>
10 <p class="card-subtitle"><%= subtitle %></p>
11 <% end %>
12 <%= yield if block_given? %>
13 </div>
14</div>erb
1<%= render "shared/card", title: "My Card", subtitle: "Optional" do %>
2 <p>Card content goes here</p>
3<% end %>Local Assigns
Check if a local variable was passed:
erb
1<!-- _article.html.erb -->
2<article class="<%= local_assigns[:class] || 'default' %>">
3 <h2><%= article.title %></h2>
4
5 <% if local_assigns[:show_author] %>
6 <span>By <%= article.user.name %></span>
7 <% end %>
8
9 <%= truncate(article.body, length: local_assigns[:length] || 150) %>
10</article>erb
1<%= render "article", article: @article, show_author: true, length: 300 %>Nested Layouts
erb
1<!-- app/views/layouts/admin.html.erb -->
2<%= content_for :body do %>
3 <div class="admin-wrapper">
4 <%= render "admin/sidebar" %>
5 <main class="admin-content">
6 <%= yield %>
7 </main>
8 </div>
9<% end %>
10<%= render template: "layouts/application" %>Partials and layouts are essential for building maintainable Rails views!
