Getting started

# 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:

```ruby
class User < ApplicationRecord
  belongs_to :country
  enum :role, { member: 0, admin: 1 }

  validates :email, presence: true, uniqueness: true
  validates :bio, length: { maximum: 400 }
end
```

## Render 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:

```ruby
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"
end
```

Each `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:

```ruby
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
end
```

```ruby
render UserForm.new(model: @user)
```

`form_options` defaults are inherited and merged down the subclass chain; instance arguments win. See [Form classes](https://phlex-forms.zoolutions.llc/docs/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:

```ruby
class UserForm < Forms::Base
  live model: User, debounce: 300

  def fields
    field :email
    field :role
    field :country
    field :bio, hint: "Optional"
    submit :primary
  end
end
```

Nothing is persisted by live validation — native submit and your controller stay authoritative. The full story (touched tracking, whitelists, constraints) is on [Live validation](https://phlex-forms.zoolutions.llc/docs/live-validation).