Quick start
From a model to a live-validating form class in four steps.
Start from the model#
Everything phlex-forms infers comes from here — column types, the enum, the association, the validators:
class User < ApplicationRecord
belongs_to :country
enum :role, { member: 0, admin: 1 }
validates :email, presence: true, uniqueness: true
validates :bio, length: { maximum: 400 }
endRender an inline form#
Form(model:) derives the scope, URL, and method from the record (POST for a new record, PATCH + hidden _method for a persisted one), emits the CSRF token, and yields itself as the builder:
Form(model: @user) do |f|
f.field :email # type=email, required — both inferred
f.field :role # enum → select: Member / Admin
f.field :country # belongs_to → select, name="user[country_id]"
f.field :bio, hint: "Optional" # text column → textarea, maxlength=400
f.submit :primary # "Create User" / "Update User"
endEach field call renders a labeled control with the error (when the model has one) or the hint beneath it. Server round-trip errors just work: re-render the form with the invalid record and every failed field shows its message.
Promote it to a form class#
The same form as a reusable, testable object — self is the form, so the f. prefix disappears:
class UserForm < Forms::Base
form_options :spaced, field_variants: [:primary]
def fields
field :email
field :role
field :country
field :bio, hint: "Optional"
submit :primary
end
endrender UserForm.new(model: @user)form_options defaults are inherited and merged down the subclass chain; instance arguments win. See Form classes.
Go live#
With phlex-reactive in the bundle, one macro turns the form into a server-truth live validator — blur a field and the real validators run (including that uniqueness check), the errors morph in, and the caret never moves:
class UserForm < Forms::Base
live model: User, debounce: 300
def fields
field :email
field :role
field :country
field :bio, hint: "Optional"
submit :primary
end
endNothing is persisted by live validation — native submit and your controller stay authoritative. The full story (touched tracking, whitelists, constraints) is on Live validation.