123456789_123456789_123456789_123456789_123456789_

DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.

Action View Overview

After reading this guide, you will know:


What is Action View?

Action View is the V inMVC.Action Controller and Action View work together to handle web requests. Action Controller is concerned with communicating with the model layer (of MVC) and retrieving data. Action View is then responsible for rendering a response body to the web request using that data.

By default, Action View templates (also referred to simply as "views") are written using Embedded Ruby (ERB), which allows using Ruby code within HTML documents.

Action View provides manyhelper methods for dynamically generating HTML tags for forms, dates, and strings. It's also possible to add custom helpers to your application as needed.

NOTE: Action View can make use of Active Model features liketo_param</a> andto_partial_path</a> to simplify code. That doesn't mean Action View depends on Active Model. Action View is an independent package that can be used with any Ruby library.

Using Action View with Rails

Action View templates (aka "views") are stored in subdirectories in the app/views directory. There is a subdirectory matching the name of each controller. The view files inside that subdirectory are used to render specific views as a response to controller actions.

For example, when you use scaffolding to generate an article resource, Rails generates the following files in app/views/articles:

$ bin/rails generate scaffold article
      [...]
      invoke  scaffold_controller
      create    app/controllers/articles_controller.rb
      invoke    erb
      create      app/views/articles
      create      app/views/articles/index.html.erb
      create      app/views/articles/edit.html.erb
      create      app/views/articles/show.html.erb
      create      app/views/articles/new.html.erb
      create      app/views/articles/_form.html.erb
      [...]

The file names follow a Rails naming convention. They share their name with the associated controller action. For example the index.html.erb, edit.html.erb, etc.

By following this naming convention, Rails will automatically find and render the matching view at the end of a controller action, without you having to specify it. For example, the index action in the articles_controller.rb will automatically render the index.html.erb view inside the app/views/articles/ directory. The name and the location of the file are both important.

The final HTML returned to the client is composed of a combination of the .html.erb ERB file, a layout template that wraps it, and all the partials that the ERB file may reference. In the rest of this guide, you will find more details about each of the three components: Templates, Partials, Layouts.

Templates

Action View templates can be written in different formats. Rails uses the file extension to determine the view's format and templating system.

For example, a file with a .html.erb extension uses ERB to build an HTML response.Jbuilder templates that generate JSON will have the extension .json.jbuilder. And an XML template usingBuilder</a> would use .xml.builder.

Other libraries may add more templating engines and formats.

ERB

An ERB template is a way to sprinkle Ruby code within static HTML using special ERB tags like <% %> and <%= %>.

When Rails processes the ERB view templates ending with .html.erb, it evaluates the embedded Ruby code and replaces the ERB tags with the dynamic output. That dynamic content is combined with the static HTML markup to form the final HTML response.

Within an ERB template, Ruby code can be included using both <% %> and <%= %> tags. The <% %> tag (without the =) is used when you want to execute Ruby code but not directly output the result, such as conditions or loops. The tag <%= %> is used for Ruby code that generates an output and you want that output rendered within the template, such as a model attribute like person.name in this example:

<h1>Names</h1>
<% @people.each do |person| %>
  Name: <%= person.name %><br>
<% end %>

The loop is set up using regular embedding tags (<% %>) and the name is inserted using the output embedding tags (<%= %>).

Note that functions such as print and puts won't be rendered to the view with ERB templates. So something like this would not work:

<%# WRONG %>
Hi, Mr. <% puts "Frodo" %>

The above example shows that comments can be added in ERB within <%# %> tag.

To suppress leading and trailing whitespaces, you can use <%- -%> interchangeably with <% and %>.

Jbuilder

Jbuilder is a gem that's maintained by the Rails team and included in the default Rails Gemfile. It is used to build JSON responses using templates.

If you don't have it, you can add the following to your Gemfile:

gem "jbuilder"

A Jbuilder object named json is automatically made available to templates with a .jbuilder extension.

Here is a basic example:

json.name("Alex")
json.email("alex@example.com")

would produce:

{
  "name": "Alex",
  "email": "alex@example.com"
}

See theJbuilder documentation for more examples.

Builder

Builder templates are a more programmatic alternative to ERB. It's similar to JBuilder but is used to generate XML, instead of JSON.

An XmlMarkup object named xml is automatically made available to templates with a .builder extension.

Here is a basic example:

xml.em("emphasized")
xml.em { xml.b("emph & bold") }
xml.a("A Link", "href" => "https://rubyonrails.org")
xml.target("name" => "compile", "option" => "fast")

which would produce:

<em>emphasized</em>
<em><b>emph &amp; bold</b></em>
<a href="https://rubyonrails.org">A link</a>
<target option="fast" name="compile" />

Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:

xml.div {
  xml.h1(@person.name)
  xml.p(@person.bio)
}

would produce something like:

<div>
  <h1>David Heinemeier Hansson</h1>
  <p>A product of Danish Design during the Winter of '79...</p>
</div>

SeeBuilder documentation for more examples.

Template Compilation

By default, Rails will compile each template to a method to render it. In the development environment, when you alter a template, Rails will check the file's modification time and recompile it.

There is also Fragment Caching for when different parts of the page need to be cached and expired separately. Learn more about it in the caching guide.

Partials

Partial templates - usually just called "partials" - are a way of breaking up the view templates into smaller reusable chunks. With partials, you can extract a piece of code from your main template to a separate smaller file, and render that file in the main template. You can also pass data to the partial files from the main template.

Let's see this in action with some examples:

Rendering Partials

To render a partial as part of a view, you use therender</a> method within the view:

<%= render "product" %>

This will look for a file named _product.html.erb in the same folder to render within that view. If no such file exists, Rails will look for it under the app/view/application/ folder. This makes app/views/application/ a great place for your shared partials.

NOTE: Rails doesn't automatically create app/views/application/. You'll need to create this folder yourself.

NOTE: Refer to theLayouts and Rendering guide for further information on the lookup hierarchy of partials and template.

Partial file names start with leading underscore character by convention. The file name distinguishes partials from regular views. However, no underscore is used when referring to partials for rendering within a view. This is true even when you reference a partial from another directory:

<%= render "admin/control_panel" %>

That code will look for and render a partial named _control_panel.html.erb in app/views/admin/.

Using Partials to Simplify Views

One way to use partials is to treat them as the equivalent of methods. A way to move details out of a view so that you can grasp what's going on more easily. For example, you might have a view that looks like this:

<%= render "application/ad_banner" %>

<h1>Products</h1>

<p>Here are a few of our fine products:</p>
<% @products.each do |product| %>
  <%= render partial: "product", locals: { product: product } %>
<% end %>

<%= render "application/footer" %>

Here, the _ad_banner.html.erb and _footer.html.erb partials could contain content that is shared among many pages in your application. You don't need to see the details of these sections when you're focused on a Products' page.

The above example also uses the _product.html.erb partial. This partial contains details for rendering an individual product and is used to render each product in the collection @products.

Passing Data to Partials with locals Option

When rendering a partial, you can pass data to the partial from the rendering view. You use the locals: options hash for this. Each key in the locals: option is available as a partial-local variable:

<%# app/views/products/show.html.erb %>

<%= render partial: "product", locals: { my_product: @product } %>

<%# app/views/products/_product.html.erb %>

<%= tag.div id: dom_id(my_product) do %>
  <h1><%= my_product.name %></h1>
<% end %>

A "partial-local variable" is a variable that is local to a given partial and only available from within that partial. In the above example, my_product is a partial-local variable. It was assigned the value of @product when passed to the partial from the original view.

Note that typically we'd simply call this local variable product. We are using my_product to distinguish it from the instance variable name and template name in this example.

Since locals is a hash, you can pass in multiple variables as needed, like locals: { my_product: @product, my_reviews: @reviews }.

However, if a template refers to a variable that isn't passed into the view as part of the locals: option, the template will raise an ::ActionView::Template::Error:

<%# app/views/products/_product.html.erb %>

<%= tag.div id: dom_id(my_product) do %>
  <h1><%= my_product.name %></h1>

  <%# => raises ActionView::Template::Error for `product_reviews` %>
  <% product_reviews.each do |review| %>
    <%# ... %>
  <% end %>
<% end %>

Using local_assigns

Each partial has a method called local_assigns available. You can use this method to access keys passed via the locals: option. If a partial was not rendered with :some_key set, the value of local_assigns will be nil within the partial.

For example, product_reviews is nil in the below example since only product is set in locals::

<%# app/views/products/show.html.erb %>

<%= render partial: "product", locals: { product: @product } %>

<%# app/views/products/_product.html.erb %>

<% local_assigns[:product]          # => "#<Product:0x0000000109ec5d10>" %>
<% local_assigns[:product_reviews]  # => nil %>

One use case for local_assigns is optionally passing in a local variable and then conditionally performing an action in the partial based on whether the local variable is set. For example:

<% if local_assigns[:redirect] %>
  <%= form.hidden_field :redirect, value: true %>
<% end %>

Another example from Active Storage's _blob.html.erb. This one sets the size based on whether in_gallery local variable is set when rendering the partial that contains this line:

<%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>

render without partial and locals Options

In the above examples, render takes 2 options: partial and locals. But if these are the only options you need to use, you can skip the keys, partial and locals, and specify the values only.

For example, instead of:

<%= render partial: "product", locals: { product: @product } %>

You can write:

<%= render "product", product: @product %>

You can also use this shorthand based on conventions:

<%= render @product %>

This will look for a partial named _product.html.erb in app/views/products/, as well as pass a local named product set to the value @product.

NOTE: Under the hood, Rails calls to_partial_path on the model to determine which partial to render. You can override this method to customize the partial if required.

The as and object Options

By default, objects passed to the template are in a local variable with the same name as the template. So, given:

<%= render @product %>

within the _product.html.erb partial you'll get @product instance variable in the local variable product, as if you had written:

<%= render partial: "product", locals: { product: @product } %>

The object option can be used to specify a different name. This is useful when the template's object is elsewhere (e.g. in a different instance variable or in a local variable).

For example, instead of:

<%= render partial: "product", locals: { product: @item } %>

you can write:

<%= render partial: "product", object: @item %>

This assigns the instance variable @item to a partial local variable named product. What if you wanted to change the local variable name from the default product to something else? You can use the :as option for that.

With the as option, you can specify a different name for the local variable like this:

<%= render partial: "product", object: @item, as: "item" %>

This is equivalent to

<%= render partial: "product", locals: { item: @item } %>

Rendering Collections

It's common for a view to iterate over a collection, such as @products, and render a partial template for each object in the collection. This pattern has been implemented as a single method that accepts an array and renders a partial for each one of the elements in the array.

For example, to render all products:

<% @products.each do |product| %>
  <%= render partial: "product", locals: { product: product } %>
<% end %>

can be rewritten in a single line:

<%= render partial: "product", collection: @products %>

When a partial is called with a collection, the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, since the partial is _product.html.erb, you can use product to refer to the collection member that is being rendered.

When the collection you wish to render contains objects that respond to to_partial_path, such as Active Record and Active Model instances, you can use the following shorthand:

<%= render @products %>

Rails determines the partial for each object by calling to_partial_path on it. Conventionally, for Active Record objects, this is the name of the model: products/_product.html.erb for Product.

A collection can be composed of different types of objects and Rails will render the corresponding partial for each one.

<%# customers/_customer.html.erb -%>
<p>Customer: <%= customer.name %></p>

<%# employees/_employee.html.erb -%>
<p>Employee: <%= employee.name %></p>
<%# index.html.erb -%>
<h1>Contacts</h1>

<%# Rails will render the matching partial for customer and employee objects -%>
<%= render [customer1, employee1, customer2, employee2] %>

If the collection is empty, render will return nil so you can define a fallback.

<h1>Products</h1>
<%= render(@products) || "There are no products available." %>

Spacer Templates

You can also specify a second partial to be rendered between instances of the main partial by using the :spacer_template option:

<%= render partial: @products, spacer_template: "product_ruler" %>

Rails will render the _product_ruler.html.erb partial (with no data passed to it) between each pair of _product.html.erb partials.

Iteration Variables

Rails injects two additional variables into each partial when rendering a collection: _counter and _iteration.

<%# app/views/products/index.html.erb -%>
<%= render @products %>

<%# app/views/products/_product.html.erb -%>
<ul>
  <li>Counter: <%= product_counter %></li>
  <li>Iteration: <%= product_iteration %></li>
</ul>

The product_counter is the index of the element being rendered within the collection. The product_iteration is an instance ofActionView::PartialIteration</a> which can be used to determine if it's the first or last element in the collection.

When using the as: option, these variable names will also change. For example, using as: :item will create variables called item_counter and item_iteration.

NOTE: The following two sections,Strict Locals and Local Assigns with Pattern Matching are more advanced features of using partials, included here for completeness.

local_assigns with Pattern Matching

Since local_assigns is a Hash, it's compatible with Ruby 3.1's pattern matching assignment operator:

local_assigns => { product:, **options }
product # => "#<Product:0x0000000109ec5d10>"
options # => {}

When keys other than :product are assigned into a partial-local Hash variable, they can be splatted into helper method calls:

<%# app/views/products/_product.html.erb %>

<% local_assigns => { product:, **options } %>

<%= tag.div id: dom_id(product), **options do %>
  <h1><%= product.name %></h1>
<% end %>

<%# app/views/products/show.html.erb %>

<%= render "products/product", product: @product, class: "card" %>
<%# => <div id="product_1" class="card">
  #      <h1>A widget</h1>
  #    </div>
%>

Pattern matching assignment also supports variable renaming:

local_assigns => { product: record }
product             # => "#<Product:0x0000000109ec5d10>"
record              # => "#<Product:0x0000000109ec5d10>"
product == record   # => true

You can also conditionally read a variable, then fall back to a default value when the key isn't part of the locals: options, using fetch:

<%# app/views/products/_product.html.erb %>

<% local_assigns.fetch(:related_products, []).each do |related_product| %>
  <%# ... %>
<% end %>

Combining Ruby 3.1's pattern matching assignment with calls toHash#with_defaults enables compact partial-local default variable assignments:

<%# app/views/products/_product.html.erb %>

<% local_assigns.with_defaults(related_products: []) => { product:, related_products: } %>

<%= tag.div id: dom_id(product) do %>
  <h1><%= product.name %></h1>

  <% related_products.each do |related_product| %>
    <%# ... %>
  <% end %>
<% end %>

Strict Locals

Action View partials are compiled into regular Ruby methods under the hood. Because it is impossible in Ruby to dynamically create local variables, every single combination of locals passed to a partial requires compiling another version:

<%# app/views/articles/show.html.erb %>

<%= render partial: "article", layout: "box", locals: { article: @article } %>
<%= render partial: "article", layout: "box", locals: { article: @article, theme: "dark" } %>

The above snippet will cause the partial to be compiled twice, taking more time and using more memory.

def _render_template_2323231_article_show(buffer, local_assigns, article:)
  # ...
end

def _render_template_3243454_article_show(buffer, local_assigns, article:, theme:)
  # ...
end

When the number of combinations is small, it's not really a problem, but if it's large it can waste a sizeable amount of memory and take a long time to compile. To counteract this you can use strict locals to define the compiled partial signature, and ensure only a single version of the partial is compiled:

<%# locals: (article:, theme: "light") -%>
...

You can enforce how many and which locals a template accepts, set default values, and more with a locals: signature, using the same syntax as Ruby method signatures.

Here are some examples of the locals: signature:

<%# app/views/messages/_message.html.erb %>

<%# locals: (message:) -%>
<%= message %>

The above makes message a required local variable. Rendering the partial without a :message local variable argument will raise an exception:

render "messages/message"
# => ActionView::Template::Error: missing local: :message for app/views/messages/_message.html.erb

If a default value is set then it can be used if message is not passed in locals::

<%# app/views/messages/_message.html.erb %>

<%# locals: (message: "Hello, world!") -%>
<%= message %>

Rendering the partial without a :message local variable uses the default value set in the locals: signature:

render "messages/message"
# => "Hello, world!"

Rendering the partial with local variables not specified in the local: signature will also raise an exception:

render "messages/message", unknown_local: "will raise"
# => ActionView::Template::Error: unknown local: :unknown_local for app/views/messages/_message.html.erb

You can allow optional local variable arguments with the double splat ** operator:

<%# app/views/messages/_message.html.erb %>

<%# locals: (message: "Hello, world!", **attributes) -%>
<%= tag.p(message, **attributes) %>

Or you can disable locals entirely by setting the locals: to empty ():

<%# app/views/messages/_message.html.erb %>

<%# locals: () %>

Rendering the partial with any local variable arguments will raise an exception:

render "messages/message", unknown_local: "will raise"
# => ActionView::Template::Error: no locals accepted for app/views/messages/_message.html.erb

WARNING: When using strict locals with collection rendering, you need to explicity allow the _counter and _iteration variables or they will not be set: <%# locals: (product_counter: nil, product_iteration: nil). The nil default values are needed so the partial doesn't break when rendered outside of collections, where these two variables will not be set.

Action View will process the locals: signature in any templating engine that supports #-prefixed comments, and will read the signature from any line in the partial.

CAUTION: Only keyword arguments are supported. Defining positional or block arguments will raise an Action View Error at render-time.

The local_assigns method does not contain default values specified in the local: signature. To access a local variable with a default value that is named the same as a reserved Ruby keyword, like class or if, the values can be accessed through binding.local_variable_get:

<%# locals: (class: "message") %>
<div class="<%= binding.local_variable_get(:class) %>">...</div>

Layouts

Layouts can be used to render a common view template around the results of Rails controller actions. A Rails application can have multiple layouts that pages can be rendered within.

For example, an application might have one layout for a logged in user and another for the marketing part of the site. The logged in user layout might include top-level navigation that should be present across many controller actions. The sales layout for a SaaS app might include top-level navigation for things like "Pricing" and "Contact Us" pages. Different layouts can have a different header and footer content.

To find the layout for the current controller action, Rails first looks for a file in app/views/layouts with the same base name as the controller. For example, rendering actions from the ProductsController class will use app/views/layouts/products.html.erb.

Rails will use app/views/layouts/application.html.erb if a controller-specific layout does not exist.

Here is an example of a basic layout in application.html.erb file:

<!DOCTYPE html>
<html>
<head>
  <title><%= "Your Rails App" %></title>
  <%= csrf_meta_tags %>
  <%= csp_meta_tag %>
  <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
  <%= javascript_importmap_tags %>
</head>
<body>

<nav>
  <ul>
    <li><%= link_to "Home", root_path %></li>
    <li><%= link_to "Products", products_path %></li>
    <%# Additional navigation links here %>
  </ul>
</nav>

<%= yield %>

<footer>
  <p>&copy; <%= Date.current.year %> Your Company</p>
</footer>

In the above example layout, view content will be rendered in place of <%= yield %>, and surrounded by the same ,