Skip to content
Jbuilder: generate JSON objects with a Builder-style DSL
Latest commit b5c5b3b Jun 16, 2016 @rwz rwz committed on GitHub Merge pull request #339 from tduek/cleanup-confusing-code
Cleanup: remove relic and confusing 'app' argument


Jbuilder gives you a simple DSL for declaring JSON structures that beats massaging giant hash structures. This is particularly helpful when the generation process is fraught with conditionals and loops. Here's a simple example:

# app/views/message/show.json.jbuilder

json.content format_content(@message.content)
json.(@message, :created_at, :updated_at) do
  json.email_address @message.creator.email_address_with_name
  json.url url_for(@message.creator, format: :json)

if current_user.admin?
  json.visitors calculate_visitors(@message)

json.comments @message.comments, :content, :created_at

json.attachments @message.attachments do |attachment|
  json.filename attachment.filename
  json.url url_for(attachment)

This will build the following structure:

  "content": "<p>This is <i>serious</i> monkey business</p>",
  "created_at": "2011-10-29T20:45:28-05:00",
  "updated_at": "2011-10-29T20:45:28-05:00",

  "author": {
    "name": "David H.",
    "email_address": "'David Heinemeier Hansson' <>",
    "url": ""

  "visitors": 15,

  "comments": [
    { "content": "Hello everyone!", "created_at": "2011-10-29T20:45:28-05:00" },
    { "content": "To you my good sir!", "created_at": "2011-10-29T20:47:28-05:00" }

  "attachments": [
    { "filename": "forecast.xls", "url": "" },
    { "filename": "presentation.pdf", "url": "" }

To define attribute and structure names dynamically, use the set! method:

json.set! :author do
  json.set! :name, 'David'

# => "author": { "name": "David" }

Top level arrays can be handled directly. Useful for index and other collection actions.

# @comments = @post.comments

json.array! @comments do |comment|
  next if comment.marked_as_spam_by?(current_user)

  json.body comment.body do

# => [ { "body": "great post...", "author": { "first_name": "Joe", "last_name": "Bloe" }} ]

You can also extract attributes from array directly.

# @people = People.all

json.array! @people, :id, :name

# => [ { "id": 1, "name": "David" }, { "id": 2, "name": "Jamie" } ]

Jbuilder objects can be directly nested inside each other. Useful for composing objects.

class Person
  # ... Class Definition ... #
  def to_builder do |person|
      person.(self, :name, :age)

class Company
  # ... Class Definition ... #
  def to_builder do |company| name
      company.president president.to_builder

company ='Doodle Corp','John Stobs', 58))!

# => {"name":"Doodle Corp","president":{"name":"John Stobs","age":58}}

You can either use Jbuilder stand-alone or directly as an ActionView template language. When required in Rails, you can create views ala show.json.jbuilder (the json is already yielded):

# Any helpers available to views are available to the builder
json.content format_content(@message.content)
json.(@message, :created_at, :updated_at) do
  json.email_address @message.creator.email_address_with_name
  json.url url_for(@message.creator, format: :json)

if current_user.admin?
  json.visitors calculate_visitors(@message)

You can use partials as well. The following will render the file views/comments/_comments.json.jbuilder, and set a local variable comments with all this message's comments, which you can use inside the partial.

json.partial! 'comments/comments', comments: @message.comments

It's also possible to render collections of partials:

json.array! @posts, partial: 'posts/post', as: :post

# or

json.partial! 'posts/post', collection: @posts, as: :post

# or

json.partial! partial: 'posts/post', collection: @posts, as: :post

# or

json.comments @post.comments, partial: 'comments/comment', as: :comment

You can pass any objects into partial templates with or without :locals option.

json.partial! 'sub_template', locals: { user: user }

# or

json.partial! 'sub_template', user: user

You can explicitly make Jbuilder object return null if you want:

json.extract! @post, :id, :title, :content, :published_at do
  if @post.anonymous?
    json.null! # or json.nil!
    json.first_name @post.author_first_name
    json.last_name @post.author_last_name

To prevent Jbuilder from including null values in the output, you can use the ignore_nil! method:

json.ignore_nil! nil "bar"
# => { "bar": "bar" }

Fragment caching is supported, it uses Rails.cache and works like caching in HTML templates:

json.cache! ['v1', @person], expires_in: 10.minutes do
  json.extract! @person, :name, :age

You can also conditionally cache a block by using cache_if! like this:

json.cache_if! !admin?, ['v1', @person], expires_in: 10.minutes do
  json.extract! @person, :name, :age

If you are rendering fragments for a collection of objects, have a look at jbuilder_cache_multi gem. It uses fetch_multi (>= Rails 4.1) to fetch multiple keys at once.

Keys can be auto formatted using key_format!, this can be used to convert keynames from the standard ruby_format to camelCase:

json.key_format! camelize: :lower
json.first_name 'David'

# => { "firstName": "David" }

You can set this globally with the class method key_format (from inside your environment.rb for example):

Jbuilder.key_format camelize: :lower

Faster JSON backends

Jbuilder uses MultiJson, which by default will use the JSON gem. That gem is currently tangled with ActiveSupport's all-Ruby #to_json implementation, which is slow (fixed in Rails >= 4.1). For faster Jbuilder rendering, you can specify something like the Yajl JSON generator instead. You'll need to include the yajl-ruby gem in your Gemfile and then set the following configuration for MultiJson:

require 'multi_json'
MultiJson.use :yajl

Contributing to Jbuilder

Jbuilder is the work of many contributors. You're encouraged to submit pull requests, propose features and discuss issues.



Jbuilder is released under the MIT License.

Something went wrong with that request. Please try again.