Ruact::Serializable
Ruact::Serializable is a mixin that lets you declare exactly which attributes of a Ruby object are safe to send to the browser as React props.
Why you need it
When you pass a Ruby object as a prop to a client component, ruact needs to serialize it to JSON. By default it calls as_json, which may expose internal fields, timestamps, or sensitive data you did not intend to send.
Ruact::Serializable gives you an explicit allowlist via ruact_props:
class Post < ApplicationRecord
include Ruact::Serializable
ruact_props :id, :title, :body
# :admin_notes, :internal_score, etc. are never sent to the browser
endBasic usage
class Post < ApplicationRecord
include Ruact::Serializable
ruact_props :id, :title, :body, :created_at
endUse in an ERB template:
<PostCard post={@post} />The browser receives only { id: 1, title: "Hello", body: "...", created_at: "..." } — nothing else.
Works with any Ruby object
Ruact::Serializable is not limited to ActiveRecord models. Include it in any Ruby class:
class UserPresenter
include Ruact::Serializable
attr_reader :id, :display_name, :avatar_url, :role
ruact_props :id, :display_name, :avatar_url, :role
def initialize(user)
@id = user.id
@display_name = user.full_name
@avatar_url = user.avatar.url
@role = user.admin? ? "admin" : "member"
end
end<UserCard user={UserPresenter.new(@user)} />ruact_props validates loudly — timing depends on the class
If you declare a prop that does not correspond to a defined method, ruact raises a clear ArgumentError naming the offending name:
class UserPresenter
include Ruact::Serializable
attr_reader :id, :name
ruact_props :id, :name, :nonexistent_method
# => ArgumentError: ruact_props: method `nonexistent_method` is not defined on UserPresenter
endThe loud check always happens; only when it fires depends on the class:
- Plain Ruby objects (POROs) are checked at class load time — a typo or missing accessor raises the moment the class is loaded, at startup rather than at render time.
- ActiveRecord models define their attribute reader methods lazily (on first access), so at the moment
ruact_props :titleruns the reader for a real column does not exist yet. To avoid rejecting a valid model at boot — and to avoid a database connection at class load — ruact defers the check for an AR model to the first serialize. A valid model boots; a bogus prop name still raises the same cleanArgumentErrorthe first time that model is rendered.
Either way the guarantee is the same: a typo'd or missing prop fails loudly, never silently.
Nested serializable objects
Props can be nested. If a declared prop returns another object that includes Ruact::Serializable, it is serialized recursively using its own ruact_props declaration:
class Author < ApplicationRecord
include Ruact::Serializable
ruact_props :id, :name
end
class Post < ApplicationRecord
include Ruact::Serializable
ruact_props :id, :title, :author
belongs_to :author
end<PostCard post={@post} />
# Sends: { id: 1, title: "Hello", author: { id: 5, name: "Alice" } }Strict serialization mode
When config.strict_serialization = true, any object passed as a prop that does not include Ruact::Serializable raises Ruact::SerializationError.
This is the recommended production setting. It prevents passing raw ActiveRecord objects (which would call as_json) and ensures all prop surfaces are explicitly declared.
# config/initializers/ruact.rb
Ruact.configure do |config|
config.strict_serialization = Rails.env.production?
endruact_props vs as_json
ruact_props | as_json | |
|---|---|---|
| Explicit allowlist | ✅ Yes | ❌ No — serializes all attributes |
| Works on POROs and ActiveRecord models | ✅ Yes | ✅ Yes |
| Validated loudly for typos/missing accessors | ✅ Yes (POROs at load, AR at first serialize) | ❌ No |
| Enforced in strict mode | ✅ Yes | ❌ Raises in strict mode |
| Nested serialization | ✅ Recursive | ✅ Recursive (via as_json chain) |
API
ruact_props(*attrs)
Class method — declares the attributes that are safe to serialize.
ruact_props :id, :name, :emailattrs— one or moreSymbolnames. Each must correspond to a reader method on the class (anattr_reader, a plain method, or an ActiveRecord column).- Raises
ArgumentErrorfor an undefined name — at class load for a PORO, at first serialize for an ActiveRecord model (whose readers are defined lazily). Either way it fails loudly. - Returns the list of declared props.
ruact_serialize
Instance method — returns a Hash of the declared props and their current values.
post = Post.find(1)
post.ruact_serialize # => { id: 1, title: "Hello", body: "..." }This is what ruact calls internally when serializing the object. You generally do not need to call it directly.