In Rails, how do you render JSON using a view?
Categories:
Rendering JSON in Rails Using Views

Discover how to leverage Rails views (.json.jbuilder or .json.erb) for flexible and maintainable JSON responses, moving beyond simple render json: calls.
Rails applications often serve JSON data to front-end clients, APIs, or mobile applications. While render json: @object is convenient for simple cases, it quickly becomes unmanageable when you need to customize attribute selection, include associations, or apply complex transformations. This article explores how to use dedicated view templates (like .json.jbuilder or .json.erb) to define your JSON structure, offering a more robust, maintainable, and flexible approach to rendering JSON in Rails.
Why Use Views for JSON?
Using views for JSON rendering brings several benefits that align with Rails' convention over configuration philosophy and promote better code organization:

Decision Flow for JSON Rendering in Rails
Rendering JSON with Jbuilder
Jbuilder is a gem included by default in new Rails applications, providing a simple DSL for building JSON structures. It allows you to define your JSON response using Ruby code within a .json.jbuilder template, making it highly readable and flexible.
First, ensure you have Jbuilder in your Gemfile (it should be there by default):
gem 'jbuilder'
Then, in your controller, you simply render the template:
class Api::V1::PostsController < ApplicationController
def show
@post = Post.find(params[:id])
# Rails automatically looks for show.json.jbuilder
# if the request format is JSON.
# No explicit render call needed if convention is followed.
end
def index
@posts = Post.all
end
end
Example controller actions for show and index
Now, let's create the Jbuilder templates. For the show action, you might want to include the post's author and comments:
json.extract! @post, :id, :title, :content, :created_at
json.author do
json.id @post.author.id
json.name @post.author.name
end
json.comments @post.comments do |comment|
json.extract! comment, :id, :body, :created_at
json.commenter comment.commenter.name
end
Jbuilder template for a single post with author and comments
For the index action, you'd typically render a collection of posts:
json.array! @posts do |post|
json.extract! post, :id, :title, :created_at
json.author_name post.author.name
json.comments_count post.comments.count
end
Jbuilder template for a collection of posts
json.extract! is great for quickly including multiple attributes. For associations, you can nest blocks or use json.child!, json.merge!, or partials (json.partial!) for reusability.Rendering JSON with ERB Templates
While Jbuilder is purpose-built for JSON, you can also use standard ERB templates (.json.erb) to generate JSON. This approach gives you full control using Ruby's built-in to_json methods or by manually constructing hashes and converting them. It's less common than Jbuilder but offers maximum flexibility if you prefer standard Ruby and don't want to learn Jbuilder's DSL.
<%# app/views/api/v1/products/show.json.erb %>
<% product_data = {
id: @product.id,
name: @product.name,
price: @product.price,
description: @product.description,
category: {
id: @product.category.id,
name: @product.category.name
}
} %>
<%= product_data.to_json %>
ERB template for a single product
<%# app/views/api/v1/products/index.json.erb %>
<% products_array = @products.map do |product|
{
id: product.id,
name: product.name,
price: product.price
}
end %>
<%= products_array.to_json %>
ERB template for a collection of products
.json.erb, be mindful of N+1 query issues, especially when iterating over collections and accessing associations. Use includes in your controller to eager-load associated data.