Blueprinter gives a Rails app JSON views without the view files
Simple, Fast, and Declarative Serialization Library for Ruby
At a glance
- What is it?
- Blueprinter is a Ruby library that turns business objects into JSON through blueprint classes, with the central idea being views, so the same object can be rendered differently for different consumers. It positions itself as a simpler and faster replacement for JBuilder or ActiveModelSerializers, and the repository has recently moved to the procore-oss organisation.
- Who is it for?
- Blueprinter suits a Rails application with several consumers of the same model, such as a web app, a mobile client and an admin tool, because views let each of them get its own shape from one blueprint.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A presenter layer, and what it replaces
Blueprinter is a JSON object presenter for Ruby that takes business objects and breaks them down into simple hashes and serializes them to JSON. It can be used in Rails in place of other serializers, and the readme names JBuilder and ActiveModelSerializers as the ones it is meant to replace.
The design commitment is that it is simple, direct and performant, and it heavily relies on the idea of views, which the documentation compares to Rails views, describing them as ways of predefining output for data in different contexts. That is the design in one sentence, and it is the reason the library is interesting rather than merely convenient: the hard part of serialization is rarely the first shape, it is the second and third shapes that every team ends up needing and that most serializers handle with conditionals.
The audience is therefore a Rails application whose models are rendered to more than one consumer. A public API, an internal admin tool and a mobile client all want the same user, and they want different things from it, and the view mechanism is how one blueprint serves all three.
The project is a Ruby gem, MIT licensed, with the documentation published on rubydoc.info under the gem name, a test workflow on GitHub Actions, a Discord channel, and a repository that carries the usual set of community files: a rubocop configuration, a Rakefile, a spec/ directory with the tests, a lib/ directory, a CHANGELOG, a contributing guide, a code of conduct, a security policy, and an EMERITUS.md file that records people who have stepped away from maintaining it.
The repository moved, and your remote is now wrong
The readme opens with something that is not documentation but housekeeping, and it matters for anyone with a fork or a clone.
There is a recent organization move, and the instruction is to change your local remote so that you pull from the new repository:
git remote set-url [previous-remote-name] [email protected]:procore-oss/blueprinter.gitand to see what your previous upstream remote was called, you run:
git remote -vThe specifics are that the project now lives under the procore-oss organisation, and the README badge, the gemspec repository field and the topic list all point at the new location. For a user of the gem, nothing changes, since the gem is fetched from the package registry. For a contributor with a fork, it is a one line fix, and the alternative is the quiet failure mode where your fork keeps working against an upstream that no longer receives commits.
The gemspec is the other place this shows up, since the homepage, the code URL and the issues URL in the project metadata are all the procore-oss URLs now.
The move itself is a signal about governance. A library that has been adopted widely enough for a company to move it under its own organisation has a different kind of maintenance backing than an individual project, and the EMERITUS.md file, which records people who have stepped away, suggests the project has a history of maintainers rather than a single owner. Neither fact is a guarantee, but both are the kind of thing worth knowing when you are choosing a dependency for an API surface.
Blueprints, identifiers and fields
The basic usage is a class and a render call. Given a user record with a list of attributes, you write a blueprint that subclasses Blueprinter::Base:
class UserBlueprint < Blueprinter::Base
identifier :uuid
fields :first_name, :last_name, :email
endand then render an instance of it:
puts UserBlueprint.render(user) # Output is a JSON stringwhich produces a hash with the identifier and the three fields, and deliberately not the password attribute that was on the model. The whitelist nature of this is the point: what appears in the output is what the blueprint says, not what the model happens to expose, and a new column on the model cannot leak into the API by accident.
The identifier has two properties that set it apart from a field, and the documentation lists them. Identifiers are always rendered and are considered their own view, the identifier view. And when rendering, identifier fields are always sorted first, before the other fields. If either of those conveniences is not wanted, the documentation notes that you can simply create the identifier fields as regular fields instead.
That is a small but useful design decision, because a stable key at the front of a JSON document is the kind of thing clients depend on, and making it automatic means a client can index into the first field without knowing which field that is.
For collections, the same render method takes an array or a collection object, and the output is a JSON array of the same shapes. And the library can be configured to treat other classes as collections, for example Mongoid::Criteria objects, or a Set.
Views are the idea the library is built on
Views are how one blueprint produces several outputs, and they are the feature to understand first.
A view is a named block inside the blueprint that declares fields and associations:
class UserBlueprint < Blueprinter::Base
identifier :uuid
field :email, name: :login
view :normal do
fields :first_name, :last_name
end
view :extended do
include_view :normal
field :address
association :projects
end
endThe extended view pulls in the normal view with include_view, and a view can include fields from another view with include_view and include_views. Rendering picks the view by name:
puts UserBlueprint.render(user, view: :extended)which gives a document with the identifier, the address, the first and last names and the renamed email, and no password and no other model attributes.
The mechanism has a consequence that is worth stating, because it is where these libraries differ. A view is a scope, not a filter, so the normal view and the extended view are not the same document with extra keys, they are two separately declared documents that happen to share a blueprint. The extended view explicitly includes the normal one, which makes the relationship visible in the code and makes the derived shape auditable. A serializer based on conditionals would give you the same result with the relationship hidden inside the conditions.
That is the difference between a view and a filter, and it is the reason the project leans on the idea of views so heavily.
Renaming, root keys and metadata
Three more features sit on top of the blueprint, and each answers a question about the shape of the response.
Renaming is done with the name option, on both fields and associations:
class UserBlueprint < Blueprinter::Base
identifier :uuid
field :email, name: :login
association :user_projects, name: :projects
endso the JSON key is login rather than email, and projects rather than user_projects. The renaming is the part of serialization that most often drifts between a model, a database column and a client, and having it in one place is the argument for a presenter layer at all.
A root key wraps the result in an object:
puts UserBlueprint.render(user, view: :normal, root: :user)which produces a document with a single user key holding the fields.
Metadata is added with the meta option, and it sits beside the root rather than inside it:
json = UserBlueprint.render(user, view: :normal, root: :user, meta: {links: [
'https://app.mydomain.com',
'https://alternate.mydomain.com'
]})so the output has a user object and a meta object holding an array of links. There is one rule here that is easy to trip over, and the documentation states it plainly: for meta attributes, a root is mandatory. The reasoning is structural, since the meta object is a sibling of the root, and without a root there is nothing for it to sit beside.
So the three features are complementary. Root for a single object envelope, meta for the pagination and links that an envelope carries, and renaming for the keys inside.
Excluding fields per view, and what the library does not cover
The last feature in the documentation is exclusion, and it is per view:
class UserBlueprint < Blueprinter::Base
identifier :uuid
field :email, name: :login
view :normal do
fields :first_name, :last_name
end
view :extended do
include_view :normal
field :address
exclude :last_name
end
endso the extended view drops last name, and the excludes option excludes multiple fields at once inline. Exclusion is a field level operation scoped to a view, which is the right place for it, since the decision to omit a field is a decision about that particular output.
What the library does not cover is worth stating, because the readme's scope is precise. Blueprinter is a JSON object presenter. There is no mention of XML, MessagePack, YAML or any other output format, so if your application needs a second serialisation format this is not the library for it, or it is the JSON half of a pair of libraries. There is also no schema validation layer described: the shapes are declared in Ruby classes, and nothing checks that they match a published schema or a client's expectations.
The other limitation is the one every Rails project has with a serializer layer. Blueprints are code, so they have a lifecycle, they need review, and they tend to accumulate as the number of views grows. The readme's own example of a user with a normal and an extended view is a small case, and a system with a dozen views per resource is a different maintenance story. The counterweight is that the views are explicit and declarative, so the surface is readable and a diff shows what changed in the output.
And the migration note from the top of the readme applies to contributors and anyone vendoring the source rather than to gem users, which is a small but useful distinction to keep in mind when you read an organisation move notice in a project you merely depend on.
Editorial conclusion
Blueprinter suits a Rails application with several consumers of the same model, such as a web app, a mobile client and an admin tool, because views let each of them get its own shape from one blueprint. It is the wrong choice if you need a format other than JSON, since the library is a JSON object presenter and the documentation covers nothing else, and a poor match if you want the output to be guaranteed against a schema, because the shapes are declared in Ruby and nothing validates them. Before adopting, decide where your blueprint classes live, since they are code with a lifecycle, and check the migration note, because the project has moved to a new organisation and a stale remote in your fork will silently stop receiving upstream work.
Frequently asked questions
How do I define and use a Blueprinter blueprint?
Subclass Blueprinter::Base, declare an identifier and your fields, and call the render method on the class with the object. The output is a JSON string containing only the fields the blueprint declared, and the identifier is always rendered and always sorted first.
What are views in Blueprinter and how do they differ from filters?
A view is a named block inside a blueprint declaring its own fields and associations, and a view can include another with include_view. It is a scope rather than a filter, so the extended view in the example is a separately declared document that deliberately includes the normal one, rather than the same document with extra keys.
How do I add a root key and metadata to the JSON output?
Pass root with a key name to wrap the result in an object, and meta with a hash to add a sibling object. The documentation notes that a root is mandatory when using meta attributes, since the meta object sits beside the root.
Can Blueprinter render objects other than arrays and ActiveRecord relations?
You can configure other classes to be treated as collections through the custom_array_like_classes setting, and the documentation shows Mongoid::Criteria and Set as examples. The render method also accepts a collection object or an array directly.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/procore-oss-blueprinter)Was this useful?