Render Method in Rails

Understanding the Render Method in Rails

In this post, we'll learn how Rails' render method works and how to use it effectively. From rendering templates, partials, and inline content to JSON and custom status codes, this post explores the different ways to render views from your controllers.

7 min read
💡
This is the sixth post in the series on Rails controllers.

When building Rails apps, we often take render for granted. It’s easy to let Rails implicitly render the default template or to drop in render :new to render a different template.

However, as you get deeper into Rails, you learn that there’s a lot more under the hood: whether you want to render JSON APIs, partials, custom inline ERB, or raw files, the render method is one of the most flexible and powerful tools in the Rails controller toolbox.

Let’s explore the various ways to use render in controller actions, starting from the basics, and then venturing into the lesser-known territory. Knowing all the ways render works helps you build better controllers, whether you’re building HTML pages, APIs, or admin panels. Once you’ve mastered the render API, you can keep your controllers focused, your templates clean, and your responses accurate.

This post focuses on the external API of the render method. If you're curious about how it works behind the scenes by reading the Rails source code, check out the following post.

Understanding the Rails Rendering Process
This article explains the Rails rendering process in the context of returning JSON data from the controller. Hopefully, it will make it clear what really happens when you call the render method from the controller.

Default Behavior: Rendering the Action's View

In the controller actions, if you don't explicitly render, Rails will render the template with the name matching the name of the controller action. This is the most common case, and Rails handles it implicitly.

For example, if you have this controller:

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

Rails will render the app/views/posts/index.html.erb view template, without you having to explicitly render anything. In fact, the index action is not even needed, and Rails will assume it by default.

This is one of the best examples of convention over configuration, a core principle of the Rails framework. This is the sort of magic people talk about when they say Rails is magic.

If you do want to be explicit, pass the name of the action as a symbol:

render :index

A common use case is when the validation fails during a create action, and we need to re-render the form using the new action.

def create
  @post = Post.new(post_params)

  if @post.save
    redirect_to @post, notice: "Post was successfully created."
  else
    render :new, status: :unprocessable_entity
  end
end

Remember that we're not redirecting to the new action above. The code under the new method won't be executed. We're simply rendering the new template.

You can also render another format like .json.erb:

render :index, formats: :json

Note: Calling the render method doesn't magically halt execution of your controller's action method. Rails will throw the DoubleRenderError if render in the middle of the action without returning explicitly.

def show
  @book = Book.find(params[:id])
  
  if @book.special?
    render action: "special_show"
  end
  
  render action: "regular_show"
end

If you don't want to execute the rest of the code that follows, return early.

def show
  @book = Book.find(params[:id])
  
  if @book.special?
    render action: "special_show"
    return
  end
  
  render action: "regular_show"
end

If you want to see the exact output of the render method, call the render_to_string method. It uses the same options as the render method but returns the string result without sending response back to the server.

Render a Template from Another Controller

To render a view template from another controller, you can specify the full path to the template using the render method. For example:

# app/controllers/orders_controller.rb

class OrdersController
  def show_summary
    @order = Order.find(params[:id])
    render template: "invoices/summary"
  end
end

This will render the app/views/invoices/summary.html.erb template, even though the action is in OrdersController. The path is relative to app/views, without the file extension.

If you want, you can also specify a full path to a template in a different controller:

render "admin/dashboard/show"

Rails will look for app/views/admin/dashboard/show.html.erb.

Rendering Text, HTML, JSON, and XML

When building APIs or responding with simple messages, you often need to render raw content. You can do this using the plain, html, json, or xml options to the render method.

Text

render plain: "Not authorized", status: :unauthorized

When using the :plain option, the text is rendered without the layout. If you want to use the layout, add the layout: true option and create the layout file .text.erb extension.

HTML

render html: "<strong>Saved!</strong>".html_safe

JSON

render json: @user

Note, the argument (@user above) must respond to to_json. The render method automatically calls to_json for you.

Or, customize the output:

render json: { id: @user.id, name: @user.name }

If you want to explore the Rails internals to understand how it really works, check out this post.

Understanding the Rails Rendering Process
This article explains the Rails rendering process in the context of returning JSON data from the controller. Hopefully, it will make it clear what really happens when you call the render method from the controller.

XML

render xml: @user

Same with JSON, the render method will call to_xml for you.

Rendering Raw File or Binary Data

If you want to send a file to the client (such as a static page), Rails can render a raw file from an absolute path.

render file: Rails.root.join('public/terms.html')

Make sure you don't use user-provided inputs otherwise it can be used to access sensitive files in your app.

By default, this renders as HTML. If you want to serve it as text:

render file: Rails.root.join('public/data.csv'), content_type: 'text/csv'

Binary content

send_data image_data, type: 'image/png', disposition: 'inline'

Or for file downloads:

send_file '/path/to/file.pdf', type: 'application/pdf', disposition: 'attachment'

Technically not render, but still part of controller response APIs. If you don't need to provide the layout, send_file is often a faster and better option.

Rendering Nothing

Sometimes, you don't want to render any body, but just want to send a header. In this case, you can use the head method, passing the HTTP status code.

head :ok

# OR

head :unauthorized

Options Available to Render

Here’s a summary of the common options you can pass to render:

Rendering mode

  • :partial – Renders a partial.
  • :inline – Renders an inline string of ERB.
  • :file – Renders a file from disk.
  • :body – Returns a raw string as the response body.
  • :plain – Renders plain text.
  • :html – Renders raw HTML (you must mark it html_safe).
  • :json – Renders JSON (calls to_json on object).
  • :xml – Renders XML (calls to_xml).
  • :js – Renders JavaScript.

Options

  • :content_type – Overrides the MIME type.
  • :assigns – Provides the hash of instance variable assignments for the template.
  • :locals – Provides the hash of local variable assignments for the template.
  • :layout - Specify a different layout or render without a layout with layout: false.
  • :location – Sets the Location header. Often used with status: :found.
  • :status – Sets the HTTP status code (e.g. :ok, :not_found).
  • :template – Renders a specific template file.
  • :action – Renders a different action’s view in the same controller.
  • :formats – Change the default format specified in the request (:html by default)
  • :variants – Look for template variations of the same format.

Before rendering, Rails looks at the action name, locale, format, variant, etc. to figure out exactly what to render. If a template doesn't exist, or is not in the correct format or variant, Rails will raise the ActionController::UnknownFormat error and render 204 No Content.

You don’t need to use all these variations daily, but knowing they exist gives you tools for every situation.

Render to String

Sometimes you want to use your view rendering engine to generate a string, without actually sending it as a response. That’s what render_to_string is for.

Common use cases include:

  • Sending HTML or text emails using the same templates as your web views.
  • Generating a downloadable PDF from a view.
  • Logging or caching the rendered output.
html = render_to_string(template: "invoices/show", layout: false)
PdfService.generate(html)

Keep in mind that render_to_string still runs the full view rendering stack (layouts, helpers, partials), so it’s not free performance-wise, but it's often the cleanest way to reuse your existing views.

Rendering Partials with Collections

Rendering collections is a performance-friendly way to output many items using a single partial. If you’re not using it yet, you should.

<%= render partial: "posts/post", collection: @posts %>

Rails will call _post.html.erb for each item in @posts, assigning each one to a post local.


That's a wrap. I hope you found this article helpful and you learned something new.

As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I reply to all emails I get from developers, and I look forward to hearing from you.

If you'd like to receive future articles directly in your email, please subscribe to my blog. Your email is respected, never shared, rented, sold or spammed. If you're already a subscriber, thank you.