# Jbuilder turns JSON views into Ruby you can actually read

> A small MIT-licensed gem that lives in the rails org and gives Rails a builder-style DSL for JSON templates, with partials, top level arrays, and a 2.15 release line from May 2026.

**rails/jbuilder** — Jbuilder: generate JSON objects with a Builder-style DSL

- Repository: https://github.com/rails/jbuilder
- Stars: 4,419 · Forks: 456
- Language: Ruby
- License: MIT
- Published: 2026-10-07 · Updated: 2026-11-03 · Language: en
- Canonical page: https://hysenlabs.com/projects/rails-jbuilder

## The problem is indentation, not JSON

The README states the motivation plainly: Jbuilder gives you a simple DSL for declaring JSON structures that beats manipulating giant hash structures, and that this helps most when the generation process is full of conditionals and loops. That is the whole pitch. Building JSON in Ruby usually means nesting hashes and then reading indentation to figure out what shape the response has, which is exactly the situation where a conditional for an admin-only field turns into a brace-counting exercise.

A view template in Jbuilder looks like this, taken straight from the README:

```ruby
json.content format_content(@message.content)
json.(@message, :created_at, :updated_at)

json.author do
  json.name @message.creator.name.familiar
  json.email_address @message.creator.email_address_with_name
  json.url url_for(@message.creator, format: :json)
end

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

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

The `if` reads like the `if` you already write in an ERB or Haml template, and the block form for nested objects means the shape is decided by indentation rather than by punctuation. The README shows the resulting structure side by side, which is useful because it confirms the mapping is direct: a block becomes a nested object, an array argument becomes an array, and no transformation happens behind your back.

What the library does not do is reduce the JSON. There is no schema, no type coercion and no validation. It is a template language for JSON, and treating it as a serializer would be a category error.

## Top level arrays are the case most serializers get wrong

Most of the DSL is about building objects, but the case that trips up hand-rolled serializers is a collection endpoint, where the top level of the response is an array rather than an object. Jbuilder handles it directly with `array!`, and the README documents both a block form that yields each element and a shorthand that extracts attributes:

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

  json.body comment.body
  json.author do
    json.first_name comment.author.first_name
    json.last_name comment.author.last_name
  end
end

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

The `next if` line is worth pausing on. Skipping an element inside the block is the filtering idiom, and it is the reason the block form exists: you can express visibility rules per element without building an intermediate array first. The attribute-shorthand form skips the block entirely and is the readable equivalent of `map(&:attributes)`, restricted to the columns you name.

There is a related case for arrays of objects without a collection: `child!`, which appends one element at a time under the current key. The README's `json.people` example nests two `json.child!` blocks and produces an array of two objects. If you find yourself reaching for it, a plain Ruby array passed to `json.people` is usually clearer when the data is static.

## Partials are where the design pays for itself

The partial support is the strongest argument for adopting Jbuilder in a Rails app with more than one JSON endpoint, because it lets an object's JSON live in one file instead of being duplicated per view. The base call passes locals explicitly:

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

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

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

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

The `as:` option names the local variable inside the partial, and the README spells out the semantics: when the value is a collection, each element is passed under that name; when it is a singular object, the object itself is. Extra locals can be passed alongside, as the README shows with `include_body: true` and `include_author: true`.

The README also warns against reading `as:` as nesting. `json.partial! @comment, as: :comment` inlines the attributes of the object, while wrapping it in a `json.comment do` block with a path string nests them under a `comment` property. Those two produce different JSON, and the difference is a common source of client-side confusion.

## Composing Jbuilder objects outside of Rails

The gem is usable stand-alone, not only as an ActionView template handler. The README's composition example wraps a Jbuilder in a plain Ruby class and nests one inside another, which is a lightweight alternative to writing a serializer object per resource:

```ruby
class Company
  # ... Class Definition ... #
  def to_builder
    Jbuilder.new do |company|
      company.name name
      company.president president.to_builder
    end
  end
end

company.to_builder.target!
```

The `to_builder` convention plus `target!` gives you a reusable definition that a view can render under a key. The tradeoff is real: once a resource's JSON lives in a Ruby class, it is no longer obvious from a template which fields an endpoint exposes, and you need tests to catch a field disappearing. Inside a Rails app, the view is usually the better home. Outside one, or when the same shape is needed by a background job and a controller, the object form earns its keep.

For runtime flexibility the README offers `set!` for dynamic key names and `merge!` for folding in an existing hash or array, so a builder can absorb a precomputed structure without losing its nesting.

## What 2.15 actually changed

The release history shows v2.15.0 published 2026-05-15 and v2.15.1 published 2026-05-26, following v2.14.1 from 2025-08-12.

## A small gem with an unusually broad search footprint

Two things about the repository itself are worth setting down. First, this is not a community fork: it lives under the `rails` GitHub organization, ships an MIT license as `MIT-LICENSE`, and its gemspec is `jbuilder.gemspec`.

Second, the repository carries no homepage field and no documentation site. The README is the whole manual, and it is organized as a feature tour rather than a reference, so a few behaviours are only described in prose. The tree is correspondingly small: `lib/`, `test/`, `gemfiles/`, `Appraisals` for testing against multiple dependency sets, `bin/`, and `.github/` for workflows. There is no `doc/` directory and no separate guides, which means questions about edge-case rendering behaviour end up in issues rather than in docs.

The repository was last pushed on 2026-06-01 and is not archived, so the maintenance question is settled: the gem is still being worked on.

## Conclusion

Jbuilder earns its place in any Rails app that renders JSON from a view template, because the DSL removes the nested-hash indentation that makes serializers hard to review and impossible to read in a diff. The four methods worth learning cold are `array!`, `partial!`, `merge!` and `set!`, and once those are in your hands the rest of the README is reference material. Start by converting one existing `.json.jbuilder` view rather than rewriting a whole API surface, and note that the repository reports no project homepage, so the README and the gemspec are the only documentation the project itself carries.

## FAQ

### Is Jbuilder still maintained?

The repository is not archived and was last pushed on 2026-06-01. Releases landed in May 2026 with v2.15.0 and v2.15.1.

### How do I return a top level JSON array from a Rails view?

Use `json.array!` at the top of the template, either with a block that yields each element or with an attribute list such as `json.array! @people, :id, :name`. The README shows a `next if` guard inside the block for skipping individual elements.

### What is the difference between `as:` and nesting a Jbuilder partial?

`as:` names the local variable the partial receives and does not add a wrapper key, so `json.partial! @comment, as: :comment` inlines the object's attributes. Wrapping the call in a `json.comment do` block with an explicit path nests those attributes under a `comment` property instead.

### Does Jbuilder work outside of Rails?

Yes. The README documents using it stand-alone, including the `to_builder` convention where a class returns a `Jbuilder.new` block that another builder nests as a value, then materializes with `target!`. In a Rails app the view is usually the better home for the same shape.

## Sources

- [Issues](https://github.com/rails/jbuilder/issues)
- [License: MIT](https://github.com/rails/jbuilder/blob/main/LICENSE)
- [rails/jbuilder on GitHub](https://github.com/rails/jbuilder)
- [README](https://github.com/rails/jbuilder/blob/main/README.md)
- [Releases](https://github.com/rails/jbuilder/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rails-jbuilder
